Merge "Makes acknowledgeNewUserDisclaimer() a @TestApi"
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index 96114dc..ffa534e 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -1376,6 +1376,11 @@
}
}
+ private boolean isAllowedBlobAccess(int uid, String packageName) {
+ return (!Process.isSupplemental(uid) && !Process.isIsolated(uid)
+ && !mPackageManagerInternal.isInstantApp(packageName, UserHandle.getUserId(uid)));
+ }
+
private class PackageChangedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -1437,8 +1442,7 @@
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
- if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
- packageName, UserHandle.getUserId(callingUid))) {
+ if (!isAllowedBlobAccess(callingUid, packageName)) {
throw new SecurityException("Caller not allowed to create session; "
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
}
@@ -1487,8 +1491,7 @@
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
- if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
- packageName, UserHandle.getUserId(callingUid))) {
+ if (!isAllowedBlobAccess(callingUid, packageName)) {
throw new SecurityException("Caller not allowed to open blob; "
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
}
@@ -1519,8 +1522,7 @@
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
- if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
- packageName, UserHandle.getUserId(callingUid))) {
+ if (!isAllowedBlobAccess(callingUid, packageName)) {
throw new SecurityException("Caller not allowed to open blob; "
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
}
@@ -1544,8 +1546,7 @@
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
- if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
- packageName, UserHandle.getUserId(callingUid))) {
+ if (!isAllowedBlobAccess(callingUid, packageName)) {
throw new SecurityException("Caller not allowed to open blob; "
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
}
@@ -1628,8 +1629,7 @@
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
- if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
- packageName, UserHandle.getUserId(callingUid))) {
+ if (!isAllowedBlobAccess(callingUid, packageName)) {
throw new SecurityException("Caller not allowed to open blob; "
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 2c085b4..77f869a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -112,6 +112,7 @@
field public static final String MANAGE_ONGOING_CALLS = "android.permission.MANAGE_ONGOING_CALLS";
field public static final String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
field public static final String MANAGE_WIFI_AUTO_JOIN = "android.permission.MANAGE_WIFI_AUTO_JOIN";
+ field public static final String MANAGE_WIFI_INTERFACES = "android.permission.MANAGE_WIFI_INTERFACES";
field public static final String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
field public static final String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
field public static final String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
@@ -342,6 +343,7 @@
field public static final int allowSingleTap = 16843353; // 0x1010259
field public static final int allowTaskReparenting = 16843268; // 0x1010204
field public static final int allowUndo = 16843999; // 0x10104df
+ field public static final int allowUntrustedActivityEmbedding;
field public static final int alpha = 16843551; // 0x101031f
field public static final int alphabeticModifiers = 16844110; // 0x101054e
field public static final int alphabeticShortcut = 16843235; // 0x10101e3
@@ -906,6 +908,7 @@
field public static final int keyboardNavigationCluster = 16844096; // 0x1010540
field public static final int keycode = 16842949; // 0x10100c5
field public static final int killAfterRestore = 16843420; // 0x101029c
+ field public static final int knownActivityEmbeddingCerts;
field public static final int knownCerts = 16844330; // 0x101062a
field public static final int lStar = 16844359; // 0x1010647
field public static final int label = 16842753; // 0x1010001
@@ -10968,6 +10971,7 @@
ctor public ActivityInfo(android.content.pm.ActivityInfo);
method public int describeContents();
method public void dump(android.util.Printer, String);
+ method @NonNull public java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts();
method public final int getThemeResource();
field public static final int COLOR_MODE_DEFAULT = 0; // 0x0
field public static final int COLOR_MODE_HDR = 2; // 0x2
@@ -10995,6 +10999,7 @@
field public static final int DOCUMENT_LAUNCH_NEVER = 3; // 0x3
field public static final int DOCUMENT_LAUNCH_NONE = 0; // 0x0
field public static final int FLAG_ALLOW_TASK_REPARENTING = 64; // 0x40
+ field public static final int FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING = 268435456; // 0x10000000
field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8
field public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000
field public static final int FLAG_CLEAR_TASK_ON_LAUNCH = 4; // 0x4
@@ -11084,6 +11089,7 @@
method public void dump(android.util.Printer, String);
method public static CharSequence getCategoryTitle(android.content.Context, int);
method public int getGwpAsanMode();
+ method @NonNull public java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts();
method public int getMemtagMode();
method public int getNativeHeapZeroInitialized();
method public int getRequestRawExternalStorageAccess();
@@ -52414,6 +52420,8 @@
method public void requestAutofill(@NonNull android.view.View, int, @NonNull android.graphics.Rect);
method public void setAutofillRequestCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.autofill.AutofillRequestCallback);
method public void setUserData(@Nullable android.service.autofill.UserData);
+ method public boolean showAutofillDialog(@NonNull android.view.View);
+ method public boolean showAutofillDialog(@NonNull android.view.View, int);
method public void unregisterCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
field public static final String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 321ebcf..5e5f387 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -851,6 +851,10 @@
field public static final int SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY = 11; // 0xb
}
+ public static class Notification.MediaStyle extends android.app.Notification.Style {
+ method @NonNull @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public android.app.Notification.MediaStyle setRemotePlaybackInfo(@NonNull CharSequence, @DrawableRes int, @Nullable android.app.PendingIntent);
+ }
+
public static final class Notification.TvExtender implements android.app.Notification.Extender {
ctor public Notification.TvExtender();
ctor public Notification.TvExtender(android.app.Notification);
@@ -5172,6 +5176,15 @@
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.Keyphrase> CREATOR;
}
+ public static final class SoundTrigger.KeyphraseRecognitionExtra implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCoarseConfidenceLevel();
+ method public int getKeyphraseId();
+ method public int getRecognitionModes();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra> CREATOR;
+ }
+
public static final class SoundTrigger.KeyphraseSoundModel extends android.hardware.soundtrigger.SoundTrigger.SoundModel implements android.os.Parcelable {
ctor public SoundTrigger.KeyphraseSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], @Nullable android.hardware.soundtrigger.SoundTrigger.Keyphrase[], int);
ctor public SoundTrigger.KeyphraseSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], @Nullable android.hardware.soundtrigger.SoundTrigger.Keyphrase[]);
@@ -8901,7 +8914,7 @@
method @Nullable public android.net.wifi.nl80211.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String);
method @NonNull public java.util.List<android.net.wifi.nl80211.NativeScanResult> getScanResults(@NonNull String, int);
method @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.TxPacketCounters getTxPacketCounters(@NonNull String);
- method public boolean notifyCountryCodeChanged();
+ method public void notifyCountryCodeChanged(@Nullable String);
method @Nullable public static android.net.wifi.nl80211.WifiNl80211Manager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
method @Deprecated public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback);
method public boolean registerCountryCodeChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.CountryCodeChangedListener);
@@ -11136,7 +11149,8 @@
method public int encodeSmdxSubjectAndReasonCode(@Nullable String, @Nullable String);
method @CallSuper public android.os.IBinder onBind(android.content.Intent);
method public abstract int onDeleteSubscription(int, String);
- method public android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean, @Nullable android.os.Bundle);
+ method @Deprecated public android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean, @Nullable android.os.Bundle);
+ method @NonNull public android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean, @NonNull android.os.Bundle);
method @Deprecated public int onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean);
method @Deprecated public abstract int onEraseSubscriptions(int);
method public int onEraseSubscriptions(int, @android.telephony.euicc.EuiccCardManager.ResetOption int);
@@ -11755,8 +11769,13 @@
public static class AlwaysOnHotwordDetector.EventPayload {
method @Nullable public android.os.ParcelFileDescriptor getAudioStream();
method @Nullable public android.media.AudioFormat getCaptureAudioFormat();
+ method @Nullable public byte[] getData();
+ method public int getDataFormat();
method @Nullable public android.service.voice.HotwordDetectedResult getHotwordDetectedResult();
- method @Nullable public byte[] getTriggerAudio();
+ method @NonNull public java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra> getKeyphraseRecognitionExtras();
+ method @Deprecated @Nullable public byte[] getTriggerAudio();
+ field public static final int DATA_FORMAT_RAW = 0; // 0x0
+ field public static final int DATA_FORMAT_TRIGGER_AUDIO = 1; // 0x1
}
public static final class AlwaysOnHotwordDetector.ModelParamRange {
@@ -11827,6 +11846,7 @@
}
public interface HotwordDetector {
+ method public default void destroy();
method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition();
method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle);
method public boolean stopRecognition();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 960fee8..bcba21b0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -300,6 +300,9 @@
public class Notification implements android.os.Parcelable {
method public boolean shouldShowForegroundImmediately();
+ field public static final String EXTRA_MEDIA_REMOTE_DEVICE = "android.mediaRemoteDevice";
+ field public static final String EXTRA_MEDIA_REMOTE_ICON = "android.mediaRemoteIcon";
+ field public static final String EXTRA_MEDIA_REMOTE_INTENT = "android.mediaRemoteIntent";
}
public final class NotificationChannel implements android.os.Parcelable {
@@ -1277,6 +1280,10 @@
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.KeyphraseMetadata> CREATOR;
}
+ public static final class SoundTrigger.KeyphraseRecognitionExtra implements android.os.Parcelable {
+ ctor public SoundTrigger.KeyphraseRecognitionExtra(int, int, int);
+ }
+
public static final class SoundTrigger.ModelParamRange implements android.os.Parcelable {
ctor public SoundTrigger.ModelParamRange(int, int);
}
@@ -2399,6 +2406,19 @@
method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public void triggerHardwareRecognitionEventForTest(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[]);
}
+ public static final class AlwaysOnHotwordDetector.EventPayload.Builder {
+ ctor public AlwaysOnHotwordDetector.EventPayload.Builder();
+ method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload build();
+ method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setAudioStream(@NonNull android.os.ParcelFileDescriptor);
+ method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setCaptureAudioFormat(@NonNull android.media.AudioFormat);
+ method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setCaptureAvailable(boolean);
+ method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setCaptureSession(int);
+ method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setData(@NonNull byte[]);
+ method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setDataFormat(int);
+ method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setHotwordDetectedResult(@NonNull android.service.voice.HotwordDetectedResult);
+ method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setKeyphraseRecognitionExtras(@NonNull java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra>);
+ }
+
public final class VisibleActivityInfo implements android.os.Parcelable {
ctor public VisibleActivityInfo(int, @NonNull android.os.IBinder);
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index cce7dd3..a58ceaa 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -215,6 +215,14 @@
public abstract boolean isSystemReady();
/**
+ * Returns package name given pid.
+ *
+ * @param pid The pid we are searching package name for.
+ */
+ @Nullable
+ public abstract String getPackageNameByPid(int pid);
+
+ /**
* Sets if the given pid has an overlay UI or not.
*
* @param pid The pid we are setting overlay UI for.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b838016..9dd206e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1330,6 +1330,32 @@
public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
/**
+ * {@link #extras} key: A {@code CharSequence} name of a remote device used for a media session
+ * associated with a {@link Notification.MediaStyle} notification. This will show in the media
+ * controls output switcher instead of the local device name.
+ * @hide
+ */
+ @TestApi
+ public static final String EXTRA_MEDIA_REMOTE_DEVICE = "android.mediaRemoteDevice";
+
+ /**
+ * {@link #extras} key: A {@code int} resource ID for an icon that should show in the output
+ * switcher of the media controls for a {@link Notification.MediaStyle} notification.
+ * @hide
+ */
+ @TestApi
+ public static final String EXTRA_MEDIA_REMOTE_ICON = "android.mediaRemoteIcon";
+
+ /**
+ * {@link #extras} key: A {@code PendingIntent} that will replace the default action for the
+ * media controls output switcher chip, associated with a {@link Notification.MediaStyle}
+ * notification. This should launch an activity.
+ * @hide
+ */
+ @TestApi
+ public static final String EXTRA_MEDIA_REMOTE_INTENT = "android.mediaRemoteIntent";
+
+ /**
* {@link #extras} key: the indices of actions to be shown in the compact view,
* as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
*/
@@ -8943,6 +8969,9 @@
private int[] mActionsToShowInCompact = null;
private MediaSession.Token mToken;
+ private CharSequence mDeviceName;
+ private int mDeviceIcon;
+ private PendingIntent mDeviceIntent;
public MediaStyle() {
}
@@ -8976,6 +9005,32 @@
}
/**
+ * For media notifications associated with playback on a remote device, provide device
+ * information that will replace the default values for the output switcher chip on the
+ * media control, as well as an intent to use when the output switcher chip is tapped,
+ * on devices where this is supported.
+ *
+ * @param deviceName The name of the remote device to display
+ * @param iconResource Icon resource representing the device
+ * @param chipIntent PendingIntent to send when the output switcher is tapped. May be
+ * {@code null}, in which case the output switcher will be disabled.
+ * This intent should open an Activity or it will be ignored.
+ * @return MediaStyle
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL)
+ @NonNull
+ public MediaStyle setRemotePlaybackInfo(@NonNull CharSequence deviceName,
+ @DrawableRes int iconResource, @Nullable PendingIntent chipIntent) {
+ mDeviceName = deviceName;
+ mDeviceIcon = iconResource;
+ mDeviceIntent = chipIntent;
+ return this;
+ }
+
+ /**
* @hide
*/
@Override
@@ -9023,6 +9078,15 @@
if (mActionsToShowInCompact != null) {
extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
}
+ if (mDeviceName != null) {
+ extras.putCharSequence(EXTRA_MEDIA_REMOTE_DEVICE, mDeviceName);
+ }
+ if (mDeviceIcon > 0) {
+ extras.putInt(EXTRA_MEDIA_REMOTE_ICON, mDeviceIcon);
+ }
+ if (mDeviceIntent != null) {
+ extras.putParcelable(EXTRA_MEDIA_REMOTE_INTENT, mDeviceIntent);
+ }
}
/**
@@ -9038,6 +9102,15 @@
if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
}
+ if (extras.containsKey(EXTRA_MEDIA_REMOTE_DEVICE)) {
+ mDeviceName = extras.getCharSequence(EXTRA_MEDIA_REMOTE_DEVICE);
+ }
+ if (extras.containsKey(EXTRA_MEDIA_REMOTE_ICON)) {
+ mDeviceIcon = extras.getInt(EXTRA_MEDIA_REMOTE_ICON);
+ }
+ if (extras.containsKey(EXTRA_MEDIA_REMOTE_INTENT)) {
+ mDeviceIntent = extras.getParcelable(EXTRA_MEDIA_REMOTE_INTENT);
+ }
}
/**
diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java
index b40fbee..eadb7e3 100644
--- a/core/java/android/app/NotificationHistory.java
+++ b/core/java/android/app/NotificationHistory.java
@@ -610,6 +610,7 @@
// Data can be too large for a transact. Write the data as a Blob, which will be written to
// ashmem if too large.
dest.writeBlob(data.marshall());
+ data.recycle();
}
public static final @NonNull Creator<NotificationHistory> CREATOR
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 5f00342..ea12942 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -920,7 +920,7 @@
public void setNavBarModeOverride(@NavBarModeOverride int navBarModeOverride) {
if (navBarModeOverride != NAV_BAR_MODE_OVERRIDE_NONE
&& navBarModeOverride != NAV_BAR_MODE_OVERRIDE_KIDS) {
- throw new IllegalArgumentException(
+ throw new UnsupportedOperationException(
"Supplied navBarModeOverride not supported: " + navBarModeOverride);
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6cb64b1..6614be7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9714,9 +9714,9 @@
* service. When zero or more packages have been added, accessibility services that are not in
* the list and not part of the system can not be enabled by the user.
* <p>
- * Calling with a null value for the list disables the restriction so that all services can be
- * used, calling with an empty list only allows the built-in system services. Any non-system
- * accessibility service that's currently enabled must be included in the list.
+ * Calling with a {@code null} value for the list disables the restriction so that all services
+ * can be used, calling with an empty list only allows the built-in system services. Any
+ * non-system accessibility service that's currently enabled must be included in the list.
* <p>
* System accessibility services are always available to the user and this method can't
* disable them.
@@ -9742,8 +9742,8 @@
/**
* Returns the list of permitted accessibility services set by this device or profile owner.
* <p>
- * An empty list means no accessibility services except system services are allowed. Null means
- * all accessibility services are allowed.
+ * An empty list means no accessibility services except system services are allowed.
+ * {@code null} means all accessibility services are allowed.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @return List of accessiblity service package names.
@@ -9788,7 +9788,7 @@
* Returns the list of accessibility services permitted by the device or profiles
* owners of this user.
*
- * <p>Null means all accessibility services are allowed, if a non-null list is returned
+ * <p>{@code null} means all accessibility services are allowed, if a non-null list is returned
* it will contain the intersection of the permitted lists for any device or profile
* owners that apply to this user. It will also include any system accessibility services.
*
@@ -9934,6 +9934,8 @@
*
* @return List of input method package names.
* @hide
+ *
+ * @see #setPermittedAccessibilityServices(ComponentName, List)
*/
@SystemApi
@RequiresPermission(anyOf = {
@@ -9954,29 +9956,30 @@
/**
* Returns the list of input methods permitted.
*
- * <p>When this method returns empty list means all input methods are allowed, if a non-empty
- * list is returned it will contain the intersection of the permitted lists for any device or
- * profile owners that apply to this user. It will also include any system input methods.
+ * <p>{@code null} means all input methods are allowed, if a non-null list is returned
+ * it will contain the intersection of the permitted lists for any device or profile
+ * owners that apply to this user. It will also include any system input methods.
*
* @return List of input method package names.
* @hide
+ *
+ * @see #setPermittedAccessibilityServices(ComponentName, List)
*/
@UserHandleAware
@RequiresPermission(allOf = {
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
android.Manifest.permission.MANAGE_USERS
}, conditional = true)
- public @NonNull List<String> getPermittedInputMethods() {
+ public @Nullable List<String> getPermittedInputMethods() {
throwIfParentInstance("getPermittedInputMethods");
- List<String> result = null;
if (mService != null) {
try {
- result = mService.getPermittedInputMethodsAsUser(myUserId());
+ return mService.getPermittedInputMethodsAsUser(myUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- return result != null ? result : Collections.emptyList();
+ return null;
}
/**
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 8bea006..0673b3a 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -18,6 +18,8 @@
import android.annotation.FloatRange;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.Activity;
import android.app.compat.CompatChanges;
@@ -35,10 +37,16 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import android.util.ArraySet;
import android.util.Printer;
+import com.android.internal.util.Parcelling;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.Set;
/**
* Information you can retrieve about a particular application
@@ -48,6 +56,9 @@
*/
public class ActivityInfo extends ComponentInfo implements Parcelable {
+ private static final Parcelling.BuiltIn.ForStringSet sForStringSet =
+ Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForStringSet.class);
+
// NOTE: When adding new data members be sure to update the copy-constructor, Parcel
// constructor, and writeToParcel.
@@ -525,6 +536,13 @@
public static final int FLAG_PREFER_MINIMAL_POST_PROCESSING = 0x2000000;
/**
+ * Bit in {@link #flags}: If set, indicates that the activity can be embedded by untrusted
+ * hosts. In this case the interactions with and visibility of the embedded activity may be
+ * limited. Set from the {@link android.R.attr#allowUntrustedActivityEmbedding} attribute.
+ */
+ public static final int FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING = 0x10000000;
+
+ /**
* @hide Bit in {@link #flags}: If set, this component will only be seen
* by the system user. Only works with broadcast receivers. Set from the
* android.R.attr#systemUserOnly attribute.
@@ -561,7 +579,8 @@
* {@link #FLAG_STATE_NOT_NEEDED}, {@link #FLAG_EXCLUDE_FROM_RECENTS},
* {@link #FLAG_ALLOW_TASK_REPARENTING}, {@link #FLAG_NO_HISTORY},
* {@link #FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS},
- * {@link #FLAG_HARDWARE_ACCELERATED}, {@link #FLAG_SINGLE_USER}.
+ * {@link #FLAG_HARDWARE_ACCELERATED}, {@link #FLAG_SINGLE_USER},
+ * {@link #FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING}.
*/
public int flags;
@@ -1080,6 +1099,13 @@
private static final long CHECK_MIN_WIDTH_HEIGHT_FOR_MULTI_WINDOW = 197654537L;
/**
+ * Optional set of a certificates identifying apps that are allowed to embed this activity. From
+ * the "knownActivityEmbeddingCerts" attribute.
+ */
+ @Nullable
+ private Set<String> mKnownActivityEmbeddingCerts;
+
+ /**
* Convert Java change bits to native.
*
* @hide
@@ -1227,6 +1253,7 @@
launchMode = orig.launchMode;
documentLaunchMode = orig.documentLaunchMode;
permission = orig.permission;
+ mKnownActivityEmbeddingCerts = orig.mKnownActivityEmbeddingCerts;
taskAffinity = orig.taskAffinity;
targetActivity = orig.targetActivity;
flags = orig.flags;
@@ -1442,6 +1469,31 @@
return mMinAspectRatio;
}
+ /**
+ * Gets the trusted host certificate digests of apps that are allowed to embed this activity.
+ * The digests are computed using the SHA-256 digest algorithm.
+ * @see android.R.attr#knownActivityEmbeddingCerts
+ */
+ @NonNull
+ public Set<String> getKnownActivityEmbeddingCerts() {
+ return mKnownActivityEmbeddingCerts == null ? Collections.emptySet()
+ : mKnownActivityEmbeddingCerts;
+ }
+
+ /**
+ * Sets the trusted host certificates of apps that are allowed to embed this activity.
+ * @see #getKnownActivityEmbeddingCerts()
+ * @hide
+ */
+ public void setKnownActivityEmbeddingCerts(@NonNull Set<String> knownActivityEmbeddingCerts) {
+ // Convert the provided digest to upper case for consistent Set membership
+ // checks when verifying the signing certificate digests of requesting apps.
+ mKnownActivityEmbeddingCerts = new ArraySet<>();
+ for (String knownCert : knownActivityEmbeddingCerts) {
+ mKnownActivityEmbeddingCerts.add(knownCert.toUpperCase(Locale.US));
+ }
+ }
+
private boolean isChangeEnabled(long changeId) {
return CompatChanges.isChangeEnabled(changeId, applicationInfo.packageName,
UserHandle.getUserHandleForUid(applicationInfo.uid));
@@ -1573,6 +1625,9 @@
if (supportsSizeChanges) {
pw.println(prefix + "supportsSizeChanges=true");
}
+ if (mKnownActivityEmbeddingCerts != null) {
+ pw.println(prefix + "knownActivityEmbeddingCerts=" + mKnownActivityEmbeddingCerts);
+ }
super.dumpBack(pw, prefix, dumpFlags);
}
@@ -1618,6 +1673,7 @@
dest.writeFloat(mMaxAspectRatio);
dest.writeFloat(mMinAspectRatio);
dest.writeBoolean(supportsSizeChanges);
+ sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags);
}
/**
@@ -1739,6 +1795,10 @@
mMaxAspectRatio = source.readFloat();
mMinAspectRatio = source.readFloat();
supportsSizeChanges = source.readBoolean();
+ mKnownActivityEmbeddingCerts = sForStringSet.unparcel(source);
+ if (mKnownActivityEmbeddingCerts.isEmpty()) {
+ mKnownActivityEmbeddingCerts = null;
+ }
}
/**
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 567f649..2528e16 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -35,6 +35,7 @@
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Printer;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
@@ -52,7 +53,9 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Locale;
import java.util.Objects;
+import java.util.Set;
import java.util.UUID;
/**
@@ -62,6 +65,8 @@
*/
public class ApplicationInfo extends PackageItemInfo implements Parcelable {
private static ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(ForBoolean.class);
+ private static final Parcelling.BuiltIn.ForStringSet sForStringSet =
+ Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForStringSet.class);
/**
* Default task affinity of all activities in this application. See
@@ -1550,6 +1555,13 @@
*/
private int localeConfigRes;
+ /**
+ * Optional set of a certificates identifying apps that are allowed to embed activities of this
+ * application. From the "knownActivityEmbeddingCerts" attribute.
+ */
+ @Nullable
+ private Set<String> mKnownActivityEmbeddingCerts;
+
public void dump(Printer pw, String prefix) {
dump(pw, prefix, DUMP_FLAG_ALL);
}
@@ -1673,6 +1685,9 @@
}
}
pw.println(prefix + "createTimestamp=" + createTimestamp);
+ if (mKnownActivityEmbeddingCerts != null) {
+ pw.println(prefix + "knownActivityEmbeddingCerts=" + mKnownActivityEmbeddingCerts);
+ }
super.dumpBack(pw, prefix);
}
@@ -1787,6 +1802,11 @@
}
proto.end(detailToken);
}
+ if (!ArrayUtils.isEmpty(mKnownActivityEmbeddingCerts)) {
+ for (String knownCert : mKnownActivityEmbeddingCerts) {
+ proto.write(ApplicationInfoProto.KNOWN_ACTIVITY_EMBEDDING_CERTS, knownCert);
+ }
+ }
proto.end(token);
}
@@ -1837,6 +1857,7 @@
super(orig);
taskAffinity = orig.taskAffinity;
permission = orig.permission;
+ mKnownActivityEmbeddingCerts = orig.mKnownActivityEmbeddingCerts;
processName = orig.processName;
className = orig.className;
theme = orig.theme;
@@ -2006,6 +2027,7 @@
}
}
dest.writeInt(localeConfigRes);
+ sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags);
}
public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
@@ -2102,6 +2124,10 @@
}
}
localeConfigRes = source.readInt();
+ mKnownActivityEmbeddingCerts = sForStringSet.unparcel(source);
+ if (mKnownActivityEmbeddingCerts.isEmpty()) {
+ mKnownActivityEmbeddingCerts = null;
+ }
}
/**
@@ -2658,7 +2684,6 @@
return localeConfigRes;
}
-
/**
* List of all shared libraries this application is linked against. This
* list will only be set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES
@@ -2675,4 +2700,29 @@
return sharedLibraryInfos;
}
+ /**
+ * Gets the trusted host certificate digests of apps that are allowed to embed activities of
+ * this application. The digests are computed using the SHA-256 digest algorithm.
+ * @see android.R.attr#knownActivityEmbeddingCerts
+ */
+ @NonNull
+ public Set<String> getKnownActivityEmbeddingCerts() {
+ return mKnownActivityEmbeddingCerts == null ? Collections.emptySet()
+ : mKnownActivityEmbeddingCerts;
+ }
+
+ /**
+ * Sets the trusted host certificates of apps that are allowed to embed activities of this
+ * application.
+ * @see #getKnownActivityEmbeddingCerts()
+ * @hide
+ */
+ public void setKnownActivityEmbeddingCerts(@NonNull Set<String> knownActivityEmbeddingCerts) {
+ // Convert the provided digest to upper case for consistent Set membership
+ // checks when verifying the signing certificate digests of requesting apps.
+ mKnownActivityEmbeddingCerts = new ArraySet<>();
+ for (String knownCert : knownActivityEmbeddingCerts) {
+ mKnownActivityEmbeddingCerts.add(knownCert.toUpperCase(Locale.US));
+ }
+ }
}
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index 515a009..ac1bcf3 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -131,7 +131,8 @@
/** Usage: The buffer will be written to by the GPU */
public static final long USAGE_GPU_COLOR_OUTPUT = 1 << 9;
/**
- * The buffer will be used as a composer HAL overlay layer.
+ * The buffer will be used as a hardware composer overlay layer. That is, it will be displayed
+ * using the system compositor via {@link SurfaceControl}
*
* This flag is currently only needed when using
* {@link android.view.SurfaceControl.Transaction#setBuffer(SurfaceControl, HardwareBuffer)}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index b0439d0..c363909 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -61,6 +61,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Locale;
import java.util.UUID;
@@ -1576,31 +1577,58 @@
}
/**
- * Additional data conveyed by a {@link KeyphraseRecognitionEvent}
- * for a key phrase detection.
- *
- * @hide
+ * Additional data conveyed by a {@link KeyphraseRecognitionEvent}
+ * for a key phrase detection.
*/
- public static class KeyphraseRecognitionExtra implements Parcelable {
- /** The keyphrase ID */
+ public static final class KeyphraseRecognitionExtra implements Parcelable {
+ /**
+ * The keyphrase ID
+ *
+ * @hide
+ */
@UnsupportedAppUsage
public final int id;
- /** Recognition modes matched for this event */
+ /**
+ * Recognition modes matched for this event
+ *
+ * @hide
+ */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public final int recognitionModes;
- /** Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER when user identification
- * is not performed */
+ /**
+ * Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER when user identification
+ * is not performed
+ *
+ * @hide
+ */
@UnsupportedAppUsage
public final int coarseConfidenceLevel;
- /** Confidence levels for all users recognized (KeyphraseRecognitionEvent) or to
- * be recognized (RecognitionConfig) */
+ /**
+ * Confidence levels for all users recognized (KeyphraseRecognitionEvent) or to
+ * be recognized (RecognitionConfig)
+ *
+ * @hide
+ */
@UnsupportedAppUsage
@NonNull
public final ConfidenceLevel[] confidenceLevels;
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public KeyphraseRecognitionExtra(int id, @RecognitionModes int recognitionModes,
+ int coarseConfidenceLevel) {
+ this(id, recognitionModes, coarseConfidenceLevel, new ConfidenceLevel[0]);
+ }
+
+ /**
+ * @hide
+ */
@UnsupportedAppUsage
public KeyphraseRecognitionExtra(int id, int recognitionModes, int coarseConfidenceLevel,
@Nullable ConfidenceLevel[] confidenceLevels) {
@@ -1611,7 +1639,47 @@
confidenceLevels != null ? confidenceLevels : new ConfidenceLevel[0];
}
- public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseRecognitionExtra> CREATOR
+ /**
+ * The keyphrase ID associated with this class' additional data
+ */
+ public int getKeyphraseId() {
+ return id;
+ }
+
+ /**
+ * Recognition modes matched for this event
+ */
+ @RecognitionModes
+ public int getRecognitionModes() {
+ return recognitionModes;
+ }
+
+ /**
+ * Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER when user identification
+ * is not performed
+ *
+ * <p>The confidence level is expressed in percent (0% -100%).
+ */
+ public int getCoarseConfidenceLevel() {
+ return coarseConfidenceLevel;
+ }
+
+ /**
+ * Detected confidence level for users defined in a keyphrase.
+ *
+ * <p>The confidence level is expressed in percent (0% -100%).
+ *
+ * <p>The user ID is derived from the system ID
+ * {@link android.os.UserHandle#getIdentifier()}.
+ *
+ * @hide
+ */
+ @NonNull
+ public Collection<ConfidenceLevel> getConfidenceLevels() {
+ return Arrays.asList(confidenceLevels);
+ }
+
+ public static final @NonNull Parcelable.Creator<KeyphraseRecognitionExtra> CREATOR
= new Parcelable.Creator<KeyphraseRecognitionExtra>() {
public KeyphraseRecognitionExtra createFromParcel(Parcel in) {
return KeyphraseRecognitionExtra.fromParcel(in);
@@ -1632,7 +1700,7 @@
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(id);
dest.writeInt(recognitionModes);
dest.writeInt(coarseConfidenceLevel);
@@ -1657,21 +1725,28 @@
@Override
public boolean equals(@Nullable Object obj) {
- if (this == obj)
+ if (this == obj) {
return true;
- if (obj == null)
+ }
+ if (obj == null) {
return false;
- if (getClass() != obj.getClass())
+ }
+ if (getClass() != obj.getClass()) {
return false;
+ }
KeyphraseRecognitionExtra other = (KeyphraseRecognitionExtra) obj;
- if (!Arrays.equals(confidenceLevels, other.confidenceLevels))
+ if (!Arrays.equals(confidenceLevels, other.confidenceLevels)) {
return false;
- if (id != other.id)
+ }
+ if (id != other.id) {
return false;
- if (recognitionModes != other.recognitionModes)
+ }
+ if (recognitionModes != other.recognitionModes) {
return false;
- if (coarseConfidenceLevel != other.coarseConfidenceLevel)
+ }
+ if (coarseConfidenceLevel != other.coarseConfidenceLevel) {
return false;
+ }
return true;
}
@@ -1715,7 +1790,7 @@
keyphraseExtras != null ? keyphraseExtras : new KeyphraseRecognitionExtra[0];
}
- public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseRecognitionEvent> CREATOR
+ public static final @NonNull Parcelable.Creator<KeyphraseRecognitionEvent> CREATOR
= new Parcelable.Creator<KeyphraseRecognitionEvent>() {
public KeyphraseRecognitionEvent createFromParcel(Parcel in) {
return KeyphraseRecognitionEvent.fromParcelForKeyphrase(in);
diff --git a/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java
index 9772bde..2dd3aaa1 100644
--- a/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java
+++ b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java
@@ -24,6 +24,7 @@
import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.TAG_NONE;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -108,6 +109,7 @@
static final int VERSION_ADD_METERED = 4;
static final int VERSION_ADD_DEFAULT_NETWORK = 5;
static final int VERSION_ADD_OEM_MANAGED_NETWORK = 6;
+ static final int VERSION_ADD_SUB_ID = 7;
}
/**
@@ -448,6 +450,13 @@
oemNetCapabilities = NetworkTemplate.OEM_MANAGED_NO;
}
+ final int subId;
+ if (version >= IdentitySetVersion.VERSION_ADD_SUB_ID) {
+ subId = in.readInt();
+ } else {
+ subId = INVALID_SUBSCRIPTION_ID;
+ }
+
// Legacy files might contain TYPE_MOBILE_* types which were deprecated in later
// releases. For backward compatibility, record them as TYPE_MOBILE instead.
final int collapsedLegacyType = getCollapsedLegacyType(type);
@@ -457,7 +466,8 @@
.setWifiNetworkKey(networkId)
.setRoaming(roaming).setMetered(metered)
.setDefaultNetwork(defaultNetwork)
- .setOemManaged(oemNetCapabilities);
+ .setOemManaged(oemNetCapabilities)
+ .setSubId(subId);
if (type == TYPE_MOBILE && ratType != NetworkTemplate.NETWORK_TYPE_ALL) {
builder.setRatType(ratType);
}
@@ -501,10 +511,10 @@
* This is copied from {@code NetworkStatsCollection#readLegacyUid}.
* See {@code NetworkStatsService#maybeUpgradeLegacyStatsLocked}.
*
- * @param taggedData whether to read tagged data. For legacy uid files, the tagged
- * data was stored in the same binary file with non-tagged data.
- * But in later releases, these data should be kept in different
- * recorders.
+ * @param taggedData whether to read only tagged data (true) or only non-tagged data
+ * (false). For legacy uid files, the tagged data was stored in
+ * the same binary file with non-tagged data. But in later releases,
+ * these data should be kept in different recorders.
* @hide
*/
@VisibleForTesting
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 9970641..1d39089 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -401,7 +401,12 @@
/**
* All known codenames starting from {@link VERSION_CODES.Q}.
*
- * <p>This includes in development codenames as well.
+ * <p>This includes in development codenames as well, i.e. if {@link #CODENAME} is not "REL"
+ * then the value of that is present in this set.
+ *
+ * <p>If a particular string is not present in this set, then it is either not a codename
+ * or a codename for a future release. For example, during Android R development, "Tiramisu"
+ * was not a known codename.
*
* @hide
*/
diff --git a/core/java/android/os/logcat/ILogcatManagerService.aidl b/core/java/android/os/logcat/ILogcatManagerService.aidl
index 68b5679..02db274 100644
--- a/core/java/android/os/logcat/ILogcatManagerService.aidl
+++ b/core/java/android/os/logcat/ILogcatManagerService.aidl
@@ -22,5 +22,7 @@
interface ILogcatManagerService {
void startThread(in int uid, in int gid, in int pid, in int fd);
void finishThread(in int uid, in int gid, in int pid, in int fd);
+ void approve(in int uid, in int gid, in int pid, in int fd);
+ void decline(in int uid, in int gid, in int pid, in int fd);
}
diff --git a/core/java/android/service/voice/AbstractHotwordDetector.java b/core/java/android/service/voice/AbstractHotwordDetector.java
index 1922607..01d5638 100644
--- a/core/java/android/service/voice/AbstractHotwordDetector.java
+++ b/core/java/android/service/voice/AbstractHotwordDetector.java
@@ -18,6 +18,7 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
@@ -34,6 +35,9 @@
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVoiceInteractionManagerService;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
/** Base implementation of {@link HotwordDetector}. */
abstract class AbstractHotwordDetector implements HotwordDetector {
private static final String TAG = AbstractHotwordDetector.class.getSimpleName();
@@ -45,6 +49,8 @@
private final Handler mHandler;
private final HotwordDetector.Callback mCallback;
private final int mDetectorType;
+ private Consumer<AbstractHotwordDetector> mOnDestroyListener;
+ private final AtomicBoolean mIsDetectorActive;
AbstractHotwordDetector(
IVoiceInteractionManagerService managerService,
@@ -55,6 +61,7 @@
mHandler = new Handler(Looper.getMainLooper());
mCallback = callback;
mDetectorType = detectorType;
+ mIsDetectorActive = new AtomicBoolean(true);
}
/**
@@ -70,6 +77,7 @@
if (DEBUG) {
Slog.i(TAG, "#recognizeHotword");
}
+ throwIfDetectorIsNoLongerActive();
// TODO: consider closing existing session.
@@ -106,6 +114,7 @@
if (DEBUG) {
Slog.d(TAG, "updateState()");
}
+ throwIfDetectorIsNoLongerActive();
synchronized (mLock) {
updateStateLocked(options, sharedMemory, null /* callback */, mDetectorType);
}
@@ -126,6 +135,35 @@
}
}
+ void registerOnDestroyListener(Consumer<AbstractHotwordDetector> onDestroyListener) {
+ synchronized (mLock) {
+ if (mOnDestroyListener != null) {
+ throw new IllegalStateException("only one destroy listener can be registered");
+ }
+ mOnDestroyListener = onDestroyListener;
+ }
+ }
+
+ @CallSuper
+ @Override
+ public void destroy() {
+ if (!mIsDetectorActive.get()) {
+ return;
+ }
+ mIsDetectorActive.set(false);
+ synchronized (mLock) {
+ mOnDestroyListener.accept(this);
+ }
+ }
+
+ protected void throwIfDetectorIsNoLongerActive() {
+ if (!mIsDetectorActive.get()) {
+ Slog.e(TAG, "attempting to use a destroyed detector which is no longer active");
+ throw new IllegalStateException(
+ "attempting to use a destroyed detector which is no longer active");
+ }
+ }
+
private static class BinderCallback
extends IMicrophoneHotwordDetectionVoiceInteractionCallback.Stub {
private final Handler mHandler;
@@ -146,7 +184,10 @@
mHandler.sendMessage(obtainMessage(
HotwordDetector.Callback::onDetected,
mCallback,
- new AlwaysOnHotwordDetector.EventPayload(audioFormat, hotwordDetectedResult)));
+ new AlwaysOnHotwordDetector.EventPayload.Builder()
+ .setCaptureAudioFormat(audioFormat)
+ .setHotwordDetectedResult(hotwordDetectedResult)
+ .build()));
}
}
}
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index c9daf52..bec5d1b 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -23,6 +23,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityThread;
@@ -59,6 +60,9 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Locale;
/**
@@ -336,7 +340,39 @@
* Additional payload for {@link Callback#onDetected}.
*/
public static class EventPayload {
- private final boolean mTriggerAvailable;
+
+ /**
+ * Flags for describing the data format provided in the event payload.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"DATA_FORMAT_"}, value = {
+ DATA_FORMAT_RAW,
+ DATA_FORMAT_TRIGGER_AUDIO,
+ })
+ public @interface DataFormat {
+ }
+
+ /**
+ * Data format is not strictly defined by the framework, and the
+ * {@link android.hardware.soundtrigger.SoundTriggerModule} voice engine may populate this
+ * field in any format.
+ */
+ public static final int DATA_FORMAT_RAW = 0;
+
+ /**
+ * Data format is defined as trigger audio.
+ *
+ * <p>When this format is used, {@link #getCaptureAudioFormat()} can be used to understand
+ * further the audio format for reading the data.
+ *
+ * @see AlwaysOnHotwordDetector#RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
+ */
+ public static final int DATA_FORMAT_TRIGGER_AUDIO = 1;
+
+ @DataFormat
+ private final int mDataFormat;
// Indicates if {@code captureSession} can be used to continue capturing more audio
// from the DSP hardware.
private final boolean mCaptureAvailable;
@@ -348,40 +384,24 @@
private final byte[] mData;
private final HotwordDetectedResult mHotwordDetectedResult;
private final ParcelFileDescriptor mAudioStream;
+ private final List<KeyphraseRecognitionExtra> mKephraseExtras;
- EventPayload(boolean triggerAvailable, boolean captureAvailable,
- AudioFormat audioFormat, int captureSession, byte[] data) {
- this(triggerAvailable, captureAvailable, audioFormat, captureSession, data, null,
- null);
- }
-
- EventPayload(boolean triggerAvailable, boolean captureAvailable,
- AudioFormat audioFormat, int captureSession, byte[] data,
- HotwordDetectedResult hotwordDetectedResult) {
- this(triggerAvailable, captureAvailable, audioFormat, captureSession, data,
- hotwordDetectedResult, null);
- }
-
- EventPayload(AudioFormat audioFormat, HotwordDetectedResult hotwordDetectedResult) {
- this(false, false, audioFormat, -1, null, hotwordDetectedResult, null);
- }
-
- EventPayload(AudioFormat audioFormat,
- HotwordDetectedResult hotwordDetectedResult,
- ParcelFileDescriptor audioStream) {
- this(false, false, audioFormat, -1, null, hotwordDetectedResult, audioStream);
- }
-
- private EventPayload(boolean triggerAvailable, boolean captureAvailable,
- AudioFormat audioFormat, int captureSession, byte[] data,
- HotwordDetectedResult hotwordDetectedResult, ParcelFileDescriptor audioStream) {
- mTriggerAvailable = triggerAvailable;
+ private EventPayload(boolean captureAvailable,
+ @Nullable AudioFormat audioFormat,
+ int captureSession,
+ @DataFormat int dataFormat,
+ @Nullable byte[] data,
+ @Nullable HotwordDetectedResult hotwordDetectedResult,
+ @Nullable ParcelFileDescriptor audioStream,
+ @NonNull List<KeyphraseRecognitionExtra> keyphraseExtras) {
mCaptureAvailable = captureAvailable;
mCaptureSession = captureSession;
mAudioFormat = audioFormat;
+ mDataFormat = dataFormat;
mData = data;
mHotwordDetectedResult = hotwordDetectedResult;
mAudioStream = audioStream;
+ mKephraseExtras = keyphraseExtras;
}
/**
@@ -400,10 +420,12 @@
* {@link #getCaptureAudioFormat()}.
*
* @see AlwaysOnHotwordDetector#RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
+ * @deprecated Use {@link #getData()} instead.
*/
+ @Deprecated
@Nullable
public byte[] getTriggerAudio() {
- if (mTriggerAvailable) {
+ if (mDataFormat == DATA_FORMAT_TRIGGER_AUDIO) {
return mData;
} else {
return null;
@@ -411,6 +433,36 @@
}
/**
+ * Conveys the format of the additional data that is triggered with the keyphrase event.
+ *
+ * @see AlwaysOnHotwordDetector#RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
+ * @see DataFormat
+ */
+ @DataFormat
+ public int getDataFormat() {
+ return mDataFormat;
+ }
+
+ /**
+ * Gets additional raw data that is triggered with the keyphrase event.
+ *
+ * <p>A {@link android.hardware.soundtrigger.SoundTriggerModule} may populate this
+ * field with opaque data for use by system applications who know about voice
+ * engine internals. Data may be null if the field is not populated by the
+ * {@link android.hardware.soundtrigger.SoundTriggerModule}.
+ *
+ * <p>If {@link #getDataFormat()} is {@link #DATA_FORMAT_TRIGGER_AUDIO}, then the
+ * entirety of this buffer is expected to be of the format from
+ * {@link #getCaptureAudioFormat()}.
+ *
+ * @see AlwaysOnHotwordDetector#RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
+ */
+ @Nullable
+ public byte[] getData() {
+ return mData;
+ }
+
+ /**
* Gets the session ID to start a capture from the DSP.
* This may be null if streaming capture isn't possible.
* If non-null, the format of the audio that can be captured can be
@@ -464,6 +516,166 @@
public ParcelFileDescriptor getAudioStream() {
return mAudioStream;
}
+
+ /**
+ * Returns the keyphrases recognized by the voice engine with additional confidence
+ * information
+ *
+ * @return List of keyphrase extras describing additional data for each keyphrase the voice
+ * engine triggered on for this event. The ordering of the list is preserved based on what
+ * the ordering provided by {@link android.hardware.soundtrigger.SoundTriggerModule}.
+ */
+ @NonNull
+ public List<KeyphraseRecognitionExtra> getKeyphraseRecognitionExtras() {
+ return mKephraseExtras;
+ }
+
+ /**
+ * Builder class for {@link EventPayload} objects
+ *
+ * @hide
+ */
+ @TestApi
+ public static final class Builder {
+ private boolean mCaptureAvailable = false;
+ private int mCaptureSession = -1;
+ private AudioFormat mAudioFormat = null;
+ @DataFormat
+ private int mDataFormat = DATA_FORMAT_RAW;
+ private byte[] mData = null;
+ private HotwordDetectedResult mHotwordDetectedResult = null;
+ private ParcelFileDescriptor mAudioStream = null;
+ private List<KeyphraseRecognitionExtra> mKeyphraseExtras = Collections.emptyList();
+
+ public Builder() {}
+
+ Builder(SoundTrigger.KeyphraseRecognitionEvent keyphraseRecognitionEvent) {
+ setCaptureAvailable(keyphraseRecognitionEvent.isCaptureAvailable());
+ setCaptureSession(keyphraseRecognitionEvent.getCaptureSession());
+ if (keyphraseRecognitionEvent.getCaptureFormat() != null) {
+ setCaptureAudioFormat(keyphraseRecognitionEvent.getCaptureFormat());
+ }
+ setDataFormat((keyphraseRecognitionEvent.triggerInData) ? DATA_FORMAT_TRIGGER_AUDIO
+ : DATA_FORMAT_RAW);
+ if (keyphraseRecognitionEvent.getData() != null) {
+ setData(keyphraseRecognitionEvent.getData());
+ }
+ if (keyphraseRecognitionEvent.keyphraseExtras != null) {
+ setKeyphraseRecognitionExtras(
+ Arrays.asList(keyphraseRecognitionEvent.keyphraseExtras));
+ }
+ }
+
+ /**
+ * Indicates if {@code captureSession} can be used to continue capturing more audio from
+ * the DSP hardware.
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @NonNull
+ public Builder setCaptureAvailable(boolean captureAvailable) {
+ mCaptureAvailable = captureAvailable;
+ return this;
+ }
+
+ /**
+ * Sets the session ID to start a capture from the DSP.
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @NonNull
+ public Builder setCaptureSession(int captureSession) {
+ mCaptureSession = captureSession;
+ return this;
+ }
+
+ /**
+ * Sets the format of the audio obtained using {@link #getTriggerAudio()}.
+ */
+ @NonNull
+ public Builder setCaptureAudioFormat(@NonNull AudioFormat audioFormat) {
+ mAudioFormat = audioFormat;
+ return this;
+ }
+
+ /**
+ * Conveys the format of the additional data that is triggered with the keyphrase event.
+ *
+ * @see AlwaysOnHotwordDetector#RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
+ * @see DataFormat
+ */
+ @NonNull
+ public Builder setDataFormat(@DataFormat int dataFormat) {
+ mDataFormat = dataFormat;
+ return this;
+ }
+
+ /**
+ * Sets additional raw data that is triggered with the keyphrase event.
+ *
+ * <p>A {@link android.hardware.soundtrigger.SoundTriggerModule} may populate this
+ * field with opaque data for use by system applications who know about voice
+ * engine internals. Data may be null if the field is not populated by the
+ * {@link android.hardware.soundtrigger.SoundTriggerModule}.
+ *
+ * <p>If {@link #getDataFormat()} is {@link #DATA_FORMAT_TRIGGER_AUDIO}, then the
+ * entirety of this
+ * buffer is expected to be of the format from {@link #getCaptureAudioFormat()}.
+ *
+ * @see AlwaysOnHotwordDetector#RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
+ */
+ @NonNull
+ public Builder setData(@NonNull byte[] data) {
+ mData = data;
+ return this;
+ }
+
+ /**
+ * Sets {@link HotwordDetectedResult} associated with the hotword event, passed from
+ * {@link HotwordDetectionService}.
+ */
+ @NonNull
+ public Builder setHotwordDetectedResult(
+ @NonNull HotwordDetectedResult hotwordDetectedResult) {
+ mHotwordDetectedResult = hotwordDetectedResult;
+ return this;
+ }
+
+ /**
+ * Sets a stream with bytes corresponding to the open audio stream with hotword data.
+ *
+ * <p>This data represents an audio stream in the format returned by
+ * {@link #getCaptureAudioFormat}.
+ *
+ * <p>Clients are expected to start consuming the stream within 1 second of receiving
+ * the
+ * event.
+ */
+ @NonNull
+ public Builder setAudioStream(@NonNull ParcelFileDescriptor audioStream) {
+ mAudioStream = audioStream;
+ return this;
+ }
+
+ /**
+ * Sets the keyphrases recognized by the voice engine with additional confidence
+ * information
+ */
+ @NonNull
+ public Builder setKeyphraseRecognitionExtras(
+ @NonNull List<KeyphraseRecognitionExtra> keyphraseRecognitionExtras) {
+ mKeyphraseExtras = keyphraseRecognitionExtras;
+ return this;
+ }
+
+ /**
+ * Builds an {@link EventPayload} instance
+ */
+ @NonNull
+ public EventPayload build() {
+ return new EventPayload(mCaptureAvailable, mAudioFormat, mCaptureSession,
+ mDataFormat, mData, mHotwordDetectedResult, mAudioStream,
+ mKeyphraseExtras);
+ }
+ }
}
/**
@@ -993,11 +1205,14 @@
/**
* Invalidates this hotword detector so that any future calls to this result
* in an IllegalStateException.
- *
- * @hide
*/
- void invalidate() {
+ @Override
+ public void destroy() {
synchronized (mLock) {
+ if (mAvailability == STATE_KEYPHRASE_ENROLLED) {
+ stopRecognition();
+ }
+
mAvailability = STATE_INVALID;
notifyStateChangedLocked();
@@ -1009,6 +1224,7 @@
}
}
}
+ super.destroy();
}
/**
@@ -1171,8 +1387,9 @@
Slog.i(TAG, "onDetected");
}
Message.obtain(mHandler, MSG_HOTWORD_DETECTED,
- new EventPayload(event.triggerInData, event.captureAvailable,
- event.captureFormat, event.captureSession, event.data, result))
+ new EventPayload.Builder(event)
+ .setHotwordDetectedResult(result)
+ .build())
.sendToTarget();
}
@Override
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index e3bb589..dfe0f54 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -140,9 +140,7 @@
Log.d(TAG, "#detectFromDspSource");
}
HotwordDetectionService.this.onDetect(
- new AlwaysOnHotwordDetector.EventPayload(
- event.triggerInData, event.captureAvailable,
- event.captureFormat, event.captureSession, event.data),
+ new AlwaysOnHotwordDetector.EventPayload.Builder(event).build(),
timeoutMillis,
new Callback(callback));
}
diff --git a/core/java/android/service/voice/HotwordDetector.java b/core/java/android/service/voice/HotwordDetector.java
index 969ec22..96fd8bb 100644
--- a/core/java/android/service/voice/HotwordDetector.java
+++ b/core/java/android/service/voice/HotwordDetector.java
@@ -119,6 +119,17 @@
void updateState(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory);
/**
+ * Invalidates this hotword detector so that any future calls to this result
+ * in an {@link IllegalStateException}.
+ *
+ * <p>If there are no other {@link HotwordDetector} instances linked to the
+ * {@link HotwordDetectionService}, the service will be shutdown.
+ */
+ default void destroy() {
+ throw new UnsupportedOperationException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* @hide
*/
static String detectorTypeToString(int detectorType) {
diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java
index 512a654..2d662ea 100644
--- a/core/java/android/service/voice/SoftwareHotwordDetector.java
+++ b/core/java/android/service/voice/SoftwareHotwordDetector.java
@@ -77,7 +77,7 @@
if (DEBUG) {
Slog.i(TAG, "#startRecognition");
}
-
+ throwIfDetectorIsNoLongerActive();
maybeCloseExistingSession();
try {
@@ -100,6 +100,7 @@
if (DEBUG) {
Slog.i(TAG, "#stopRecognition");
}
+ throwIfDetectorIsNoLongerActive();
try {
mManagerService.stopListeningFromMic();
@@ -110,6 +111,19 @@
return true;
}
+ @Override
+ public void destroy() {
+ stopRecognition();
+ maybeCloseExistingSession();
+
+ try {
+ mManagerService.shutdownHotwordDetectionService();
+ } catch (RemoteException ex) {
+ ex.rethrowFromSystemServer();
+ }
+ super.destroy();
+ }
+
private void maybeCloseExistingSession() {
// TODO: needs to be synchronized.
// TODO: implement this
@@ -135,8 +149,11 @@
mHandler.sendMessage(obtainMessage(
HotwordDetector.Callback::onDetected,
mCallback,
- new AlwaysOnHotwordDetector.EventPayload(
- audioFormat, hotwordDetectedResult, audioStream)));
+ new AlwaysOnHotwordDetector.EventPayload.Builder()
+ .setCaptureAudioFormat(audioFormat)
+ .setAudioStream(audioStream)
+ .setHotwordDetectedResult(hotwordDetectedResult)
+ .build()));
}
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index f52c9ff..bf0cfbe 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -271,7 +271,7 @@
// It's still guaranteed to have been stopped.
// This helps with cases where the voice interaction implementation is changed
// by the user.
- safelyShutdownHotwordDetector();
+ safelyShutdownAllHotwordDetectors();
}
/**
@@ -380,11 +380,13 @@
}
synchronized (mLock) {
// Allow only one concurrent recognition via the APIs.
- safelyShutdownHotwordDetector();
+ safelyShutdownAllHotwordDetectors();
mHotwordDetector = new AlwaysOnHotwordDetector(keyphrase, locale, callback,
mKeyphraseEnrollmentInfo, mSystemService,
getApplicationContext().getApplicationInfo().targetSdkVersion,
supportHotwordDetectionService, options, sharedMemory);
+ mHotwordDetector.registerOnDestroyListener((detector) -> onDspHotwordDetectorDestroyed(
+ (AlwaysOnHotwordDetector) detector));
}
return mHotwordDetector;
}
@@ -433,10 +435,13 @@
}
synchronized (mLock) {
// Allow only one concurrent recognition via the APIs.
- safelyShutdownHotwordDetector();
+ safelyShutdownAllHotwordDetectors();
mSoftwareHotwordDetector =
new SoftwareHotwordDetector(
mSystemService, null, options, sharedMemory, callback);
+ mSoftwareHotwordDetector.registerOnDestroyListener(
+ (detector) -> onMicrophoneHotwordDetectorDestroyed(
+ (SoftwareHotwordDetector) detector));
}
return mSoftwareHotwordDetector;
}
@@ -482,51 +487,36 @@
return mKeyphraseEnrollmentInfo.getKeyphraseMetadata(keyphrase, locale) != null;
}
- private void safelyShutdownHotwordDetector() {
+ private void safelyShutdownAllHotwordDetectors() {
synchronized (mLock) {
- shutdownDspHotwordDetectorLocked();
- shutdownMicrophoneHotwordDetectorLocked();
+ if (mHotwordDetector != null) {
+ try {
+ mHotwordDetector.destroy();
+ } catch (Exception ex) {
+ Log.i(TAG, "exception destroying AlwaysOnHotwordDetector", ex);
+ }
+ }
+
+ if (mSoftwareHotwordDetector != null) {
+ try {
+ mSoftwareHotwordDetector.destroy();
+ } catch (Exception ex) {
+ Log.i(TAG, "exception destroying SoftwareHotwordDetector", ex);
+ }
+ }
}
}
- private void shutdownDspHotwordDetectorLocked() {
- if (mHotwordDetector == null) {
- return;
+ private void onDspHotwordDetectorDestroyed(@NonNull AlwaysOnHotwordDetector detector) {
+ synchronized (mLock) {
+ mHotwordDetector = null;
}
-
- try {
- mHotwordDetector.stopRecognition();
- } catch (Exception ex) {
- // Ignore.
- }
-
- try {
- mHotwordDetector.invalidate();
- } catch (Exception ex) {
- // Ignore.
- }
-
- mHotwordDetector = null;
}
- private void shutdownMicrophoneHotwordDetectorLocked() {
- if (mSoftwareHotwordDetector == null) {
- return;
+ private void onMicrophoneHotwordDetectorDestroyed(@NonNull SoftwareHotwordDetector detector) {
+ synchronized (mLock) {
+ mSoftwareHotwordDetector = null;
}
-
- try {
- mSoftwareHotwordDetector.stopRecognition();
- } catch (Exception ex) {
- // Ignore.
- }
-
- try {
- mSystemService.shutdownHotwordDetectionService();
- } catch (Exception ex) {
- // Ignore.
- }
-
- mSoftwareHotwordDetector = null;
}
/**
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index c91851a..a2938a8 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -26,6 +26,8 @@
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.FloatRange;
import android.annotation.NonNull;
@@ -890,8 +892,6 @@
* @param dimAmount Float amount between [0.0, 1.0] to dim the wallpaper.
*/
private void updateWallpaperDimming(float dimAmount) {
- mPreviousWallpaperDimAmount = mWallpaperDimAmount;
-
// Custom dim amount cannot be less than the default dim amount.
mWallpaperDimAmount = Math.max(mDefaultDimAmount, dimAmount);
// If dim amount is 0f (additional dimming is removed), then the wallpaper should dim
@@ -909,15 +909,15 @@
SurfaceControl.Transaction surfaceControlTransaction = new SurfaceControl.Transaction();
// TODO: apply the dimming to preview as well once surface transparency works in
// preview mode.
- if (!isPreview() && mShouldDim) {
+ if ((!isPreview() && mShouldDim)
+ || mPreviousWallpaperDimAmount != mWallpaperDimAmount) {
Log.v(TAG, "Setting wallpaper dimming: " + mWallpaperDimAmount);
// Animate dimming to gradually change the wallpaper alpha from the previous
// dim amount to the new amount only if the dim amount changed.
ValueAnimator animator = ValueAnimator.ofFloat(
mPreviousWallpaperDimAmount, mWallpaperDimAmount);
- animator.setDuration(mPreviousWallpaperDimAmount == mWallpaperDimAmount
- ? 0 : DIMMING_ANIMATION_DURATION_MS);
+ animator.setDuration(DIMMING_ANIMATION_DURATION_MS);
animator.addUpdateListener((ValueAnimator va) -> {
final float dimValue = (float) va.getAnimatedValue();
if (mBbqSurfaceControl != null) {
@@ -925,11 +925,19 @@
.setAlpha(mBbqSurfaceControl, 1 - dimValue).apply();
}
});
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ updateSurface(false, false, true);
+ }
+ });
animator.start();
} else {
Log.v(TAG, "Setting wallpaper dimming: " + 0);
surfaceControlTransaction.setAlpha(mBbqSurfaceControl, 1.0f).apply();
}
+
+ mPreviousWallpaperDimAmount = mWallpaperDimAmount;
}
/**
@@ -1332,6 +1340,7 @@
resetWindowPages();
mSession.finishDrawing(mWindow, null /* postDrawTransaction */);
processLocalColors(mPendingXOffset, mPendingXOffsetStep);
+ notifyColorsChanged();
}
reposition();
reportEngineShown(shouldWaitForEngineShown());
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 3cd1e0f..ce54968 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -3700,21 +3700,32 @@
/**
* Sets the buffer transform that should be applied to the current buffer.
*
+ * This can be used in combination with
+ * {@link AttachedSurfaceControl#addOnBufferTransformHintChangedListener(AttachedSurfaceControl.OnBufferTransformHintChangedListener)}
+ * to pre-rotate the buffer for the current display orientation. This can
+ * improve the performance of displaying the associated buffer.
+ *
* @param sc The SurfaceControl to update
* @param transform The transform to apply to the buffer.
* @return this
*/
public @NonNull Transaction setBufferTransform(@NonNull SurfaceControl sc,
- /* TODO: Mark the intdef */ int transform) {
+ @SurfaceControl.BufferTransform int transform) {
checkPreconditions(sc);
nativeSetBufferTransform(mNativeObject, sc.mNativeObject, transform);
return this;
}
/**
- * Updates the region for the content on this surface updated in this transaction.
+ * Updates the region for the content on this surface updated in this transaction. The
+ * damage region is the area of the buffer that has changed since the previously
+ * sent buffer. This can be used to reduce the amount of recomposition that needs
+ * to happen when only a small region of the buffer is being updated, such as for
+ * a small blinking cursor or a loading indicator.
*
- * If unspecified, the complete surface is assumed to be damaged.
+ * @param sc The SurfaceControl on which to set the damage region
+ * @param region The region to set. If null, the entire buffer is assumed dirty. This is
+ * equivalent to not setting a damage region at all.
*/
public @NonNull Transaction setDamageRegion(@NonNull SurfaceControl sc,
@Nullable Region region) {
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 3fb0fe7..2edfda5 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -28,9 +28,9 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;
-import android.view.accessibility.IAccessibilityEmbeddedConnection;
import android.view.InsetsState;
import android.view.WindowManagerGlobal;
+import android.view.accessibility.IAccessibilityEmbeddedConnection;
import java.util.Objects;
@@ -428,4 +428,11 @@
WindowManagerGlobal.getInstance().removeWindowlessRoot(mViewRoot);
mReleased = true;
}
+
+ /**
+ * @hide
+ */
+ public IBinder getFocusGrantToken() {
+ return mWm.getFocusGrantToken();
+ }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3ba305b..8236fbb 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1794,10 +1794,16 @@
}
void pokeDrawLockIfNeeded() {
- final int displayState = mAttachInfo.mDisplayState;
- if (mView != null && mAdded && mTraversalScheduled
- && (displayState == Display.STATE_DOZE
- || displayState == Display.STATE_DOZE_SUSPEND)) {
+ if (!Display.isDozeState(mAttachInfo.mDisplayState)) {
+ // Only need to acquire wake lock for DOZE state.
+ return;
+ }
+ if (mWindowAttributes.type != WindowManager.LayoutParams.TYPE_BASE_APPLICATION) {
+ // Non-activity windows should be responsible to hold wake lock by themself, because
+ // usually they are system windows.
+ return;
+ }
+ if (mAdded && mTraversalScheduled && mAttachInfo.mHasWindowFocus) {
try {
mWindowSession.pokeDrawLock(mWindow);
} catch (RemoteException ex) {
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 60ccf67..b7994db 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -3100,11 +3100,51 @@
}
/**
- * Checks the id of autofill whether supported the fill dialog.
+ * If autofill suggestions for a dialog-style UI are available for {@code view}, shows a dialog
+ * allowing the user to select a suggestion and returns {@code true}.
+ * <p>
+ * The dialog may not be available if the autofill service does not support it, or if the
+ * autofill request has not returned a response yet.
+ * <p>
+ * It is recommended to call this method the first time a user focuses on an autofill-able form,
+ * and to avoid showing the input method if the dialog is shown. If this method returns
+ * {@code false}, you should then instead show the input method (assuming that is how the
+ * view normally handles the focus event). If the user re-focuses on the view, you should not
+ * call this method again so as to not disrupt usage of the input method.
*
- * @hide
+ * @param view the view for which to show autofill suggestions. This is typically a view
+ * receiving a focus event. The autofill suggestions shown will include content for
+ * related views as well.
+ * @return {@code true} if the autofill dialog is being shown
*/
- public boolean isShowFillDialog(AutofillId id) {
+ // TODO(b/210926084): Consider whether to include the one-time show logic within this method.
+ public boolean showAutofillDialog(@NonNull View view) {
+ Objects.requireNonNull(view);
+ if (shouldShowAutofillDialog(view.getAutofillId())) {
+ // If the id matches a trigger id, this will trigger the fill dialog.
+ notifyViewEntered(view);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Like {@link #showAutofillDialog(View)} but for virtual views.
+ *
+ * @param virtualId id identifying the virtual child inside the parent view.
+ */
+ // TODO(b/210926084): Consider whether to include the one-time show logic within this method.
+ public boolean showAutofillDialog(@NonNull View view, int virtualId) {
+ Objects.requireNonNull(view);
+ if (shouldShowAutofillDialog(getAutofillId(view, virtualId))) {
+ // If the id matches a trigger id, this will trigger the fill dialog.
+ notifyViewEntered(view, virtualId, /* bounds= */ null, /* flags= */ 0);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean shouldShowAutofillDialog(AutofillId id) {
if (!hasFillDialogUiFeature() || mFillDialogTriggerIds == null) {
return false;
}
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 7e6e6fd..9a70667 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -278,6 +278,7 @@
.InputMethod_Subtype_subtypeId, 0 /* use Arrays.hashCode */))
.setIsAsciiCapable(a.getBoolean(com.android.internal.R.styleable
.InputMethod_Subtype_isAsciiCapable, false)).build();
+ a.recycle();
if (!subtype.isAuxiliary()) {
isAuxIme = false;
}
diff --git a/core/java/com/android/internal/widget/SubtitleView.java b/core/java/com/android/internal/widget/SubtitleView.java
index 21e63c5..4d6151d 100644
--- a/core/java/com/android/internal/widget/SubtitleView.java
+++ b/core/java/com/android/internal/widget/SubtitleView.java
@@ -115,6 +115,7 @@
break;
}
}
+ a.recycle();
// Set up density-dependent properties.
// TODO: Move these to a default style.
diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto
index 5c6116a..279a5d0 100644
--- a/core/proto/android/content/package_item_info.proto
+++ b/core/proto/android/content/package_item_info.proto
@@ -115,4 +115,5 @@
}
optional Detail detail = 17;
repeated string overlay_paths = 18;
+ repeated string known_activity_embedding_certs = 19;
}
diff --git a/core/proto/android/internal/binder_latency.proto b/core/proto/android/internal/binder_latency.proto
index 8b11f5b..edd9b71 100644
--- a/core/proto/android/internal/binder_latency.proto
+++ b/core/proto/android/internal/binder_latency.proto
@@ -35,6 +35,7 @@
SYSTEM_SERVER = 1;
TELEPHONY = 2;
BLUETOOTH = 3;
+ WIFI = 4;
}
enum ServiceClassName {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 19c1325..0c41f31 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1905,6 +1905,13 @@
<permission android:name="android.permission.MANAGE_WIFI_AUTO_JOIN"
android:protectionLevel="signature|privileged" />
+ <!-- Allows applications to get notified when a Wi-Fi interface request cannot
+ be satisfied without tearing down one or more other interfaces, and provide a decision
+ whether to approve the request or reject it.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MANAGE_WIFI_INTERFACES"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi @hide Allows apps to create and manage IPsec tunnels.
<p>Only granted to applications that are currently bound by the
system for creating and managing IPsec-based interfaces.
@@ -6629,6 +6636,14 @@
android:exported="false">
</activity>
+ <activity android:name="com.android.server.logcat.LogAccessConfirmationActivity"
+ android:theme="@style/Theme.Dialog.Confirmation"
+ android:excludeFromRecents="true"
+ android:process=":ui"
+ android:label="@string/log_access_confirmation_title"
+ android:exported="false">
+ </activity>
+
<activity android:name="com.android.server.notification.NASLearnMoreActivity"
android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index cb40e86..6dc975b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1048,6 +1048,24 @@
<p>The default value of this attribute is <code>false</code>. -->
<attr name="allowEmbedded" format="boolean" />
+ <!-- A reference to an array resource containing the signing certificate digests, one of which a
+ client is required to be signed with in order to embed the activity. If the client is not
+ signed with one of the certificates in the set, and the activity does not allow embedding
+ by untrusted hosts via {@link android.R.attr#allowUntrustedActivityEmbedding} flag, the
+ embedding request will fail.
+ <p>The digest should be computed over the DER encoding of the trusted certificate using the
+ SHA-256 digest algorithm.
+ <p>If only a single signer is declared this can also be a string resource, or the digest
+ can be declared inline as the value for this attribute.
+ <p>If the attribute is declared both on the application and the activity level, the value
+ on the activity level takes precedence. -->
+ <attr name="knownActivityEmbeddingCerts" format="reference|string" />
+
+ <!-- Indicate that the activity can be embedded by untrusted hosts. In this case the
+ interactions and visibility of the embedded activity may be limited.
+ <p>The default value of this attribute is <code>false</code>. -->
+ <attr name="allowUntrustedActivityEmbedding" format="boolean" />
+
<!-- Specifies whether this {@link android.app.Activity} should be shown on
top of the lock screen whenever the lockscreen is up and this activity has another
activity behind it with the {@link android.R.attr#showWhenLocked} attribute set. That
@@ -2011,6 +2029,7 @@
when the application's user data is cleared. The default value is false.
-->
<attr name="resetEnabledSettingsOnAppDataCleared" format="boolean" />
+ <attr name="knownActivityEmbeddingCerts" />
</declare-styleable>
<!-- An attribution is a logical part of an app and is identified by a tag.
@@ -3033,6 +3052,8 @@
<!-- Indicates whether the activity can be displayed on a remote device which may or
may not be running Android. -->
<attr name="canDisplayOnRemoteDevices" format="boolean"/>
+ <attr name="allowUntrustedActivityEmbedding" />
+ <attr name="knownActivityEmbeddingCerts" />
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
@@ -3073,6 +3094,8 @@
<attr name="exported" />
<attr name="parentActivityName" />
<attr name="attributionTags" />
+ <attr name="allowUntrustedActivityEmbedding" />
+ <attr name="knownActivityEmbeddingCerts" />
</declare-styleable>
<!-- The <code>meta-data</code> tag is used to attach additional
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 775527d..6eb19e1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4276,6 +4276,9 @@
<!-- URI for in call notification sound -->
<string translatable="false" name="config_inCallNotificationSound">/product/media/audio/ui/InCallNotification.ogg</string>
+ <!-- URI for camera shutter sound -->
+ <string translatable="false" name="config_cameraShutterSound">/product/media/audio/ui/camera_click.ogg</string>
+
<!-- URI for default ringtone sound file to be used for silent ringer vibration -->
<string translatable="false" name="config_defaultRingtoneVibrationSound"></string>
@@ -5697,4 +5700,75 @@
-->
<string-array name="config_dockExtconStateMapping">
</string-array>
+
+ <!-- Whether or not the monitoring on the apps' background battery drain is enabled -->
+ <bool name="config_bg_current_drain_monitor_enabled">true</bool>
+
+ <!-- The threshold of the background current drain (in percentage) to the restricted
+ standby bucket.
+ -->
+ <array name="config_bg_current_drain_threshold_to_restricted_bucket">
+ <item>2.0</item> <!-- regular device -->
+ <item>4.0</item> <!-- low ram device -->
+ </array>
+
+ <!-- The threshold of the background current drain (in percentage) to the background
+ restricted level.
+ -->
+ <array name="config_bg_current_drain_threshold_to_bg_restricted">
+ <item>4.0</item> <!-- regular device -->
+ <item>8.0</item> <!-- low ram device -->
+ </array>
+
+ <!-- The background current drain monitoring window size. -->
+ <integer name="config_bg_current_drain_window">86400</integer>
+
+ <!-- The types of battery drain we're checking on each app; if the sum of the battery drain
+ exceeds the threshold, it'll be moved to restricted standby bucket. The value must be
+ one of or combination of the definitions in AppBatteryPolicy.
+ -->
+ <integer name="config_bg_current_drain_types_to_restricted_bucket">4</integer>
+
+ <!-- The types of battery drain we're checking on each app; if the sum of the battery drain
+ exceeds the threshold, it'll be moved to background restricted level. The value must be
+ one of or combination of the definitions in AppBatteryPolicy.
+ -->
+ <integer name="config_bg_current_drain_types_to_bg_restricted">12</integer>
+
+ <!-- The power usage components we're monitoring. Must one of the definition in BatteryConsumer.
+ -->
+ <integer name="config_bg_current_drain_power_components">-1</integer>
+
+ <!-- Whether or not enable the different threshold based on the durations of
+ certain event type.
+ -->
+ <bool name="config_bg_current_drain_event_duration_based_threshold_enabled">false</bool>
+
+ <!-- The threshold of the background current drain (in percentage) to the restricted
+ standby bucket for legitimate case with higher background current drain.
+ -->
+ <array name="config_bg_current_drain_high_threshold_to_restricted_bucket">
+ <item>30.0</item> <!-- regular device -->
+ <item>60.0</item> <!-- low ram device -->
+ </array>
+
+ <!-- The threshold of the background current drain (in percentage) to the background
+ restricted level for legitimate case with higher background current drain.
+ -->
+ <array name="config_bg_current_drain_high_threshold_to_bg_restricted">
+ <item>20.0</item> <!-- regular device -->
+ <item>40.0</item> <!-- low ram device -->
+ </array>
+
+ <!-- The threshold of minimal time of hosting a foreground service with type "mediaPlayback"
+ or a media session, over the given window, so it'd subject towards the higher background
+ current drain threshold.
+ -->
+ <integer name="config_bg_current_drain_media_playback_min_duration">1800</integer>
+
+ <!-- The threshold of minimal time of hosting a foreground service with type "location"
+ over the given window, so it'd subject towards the higher background
+ current drain threshold.
+ -->
+ <integer name="config_bg_current_drain_location_min_duration">1800</integer>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index cc63fd6..3d80ca8 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3276,6 +3276,8 @@
<public name="toExtendBottom" />
<public name="tileService" />
<public name="windowSplashScreenBehavior" />
+ <public name="allowUntrustedActivityEmbedding" />
+ <public name="knownActivityEmbeddingCerts" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01de0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cb209ab1..e41aa45 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5719,6 +5719,20 @@
<!-- Title for the harmful app warning dialog. [CHAR LIMIT=40] -->
<string name="harmful_app_warning_title">Harmful app detected</string>
+ <!-- Title for the log access confirmation dialog. [CHAR LIMIT=40] -->
+ <string name="log_access_confirmation_title">System log access request</string>
+ <!-- Label for the allow button on the log access confirmation dialog. [CHAR LIMIT=20] -->
+ <string name="log_access_confirmation_allow">Only this time</string>
+ <!-- Label for the deny button on the log access confirmation dialog. [CHAR LIMIT=20] -->
+ <string name="log_access_confirmation_deny">Don\u2019t allow</string>
+
+ <!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]-->
+ <string name="log_access_confirmation_body"><xliff:g id="log_access_app_name" example="Example App">%s</xliff:g> requests system logs for functional debugging.
+ These logs might contain information that apps and services on your device have written.</string>
+
+ <!-- Privacy notice do not show [CHAR LIMIT=20] -->
+ <string name="log_access_do_not_show_again">Don\u2019t show again</string>
+
<!-- Text describing a permission request for one app to show another app's
slices [CHAR LIMIT=NONE] -->
<string name="slices_permission_request"><xliff:g id="app" example="Example App">%1$s</xliff:g> wants to show <xliff:g id="app_2" example="Other Example App">%2$s</xliff:g> slices</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e10ed24..c63e543 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3749,6 +3749,7 @@
<java-symbol type="bool" name="config_handleVolumeAliasesUsingVolumeGroups" />
<java-symbol type="dimen" name="config_inCallNotificationVolume" />
<java-symbol type="string" name="config_inCallNotificationSound" />
+ <java-symbol type="string" name="config_cameraShutterSound" />
<java-symbol type="integer" name="config_autoGroupAtCount" />
<java-symbol type="bool" name="config_dozeAlwaysOnDisplayAvailable" />
<java-symbol type="bool" name="config_dozeAlwaysOnEnabled" />
@@ -3868,6 +3869,11 @@
<java-symbol type="string" name="harmful_app_warning_title" />
<java-symbol type="layout" name="harmful_app_warning_dialog" />
+ <java-symbol type="string" name="log_access_confirmation_allow" />
+ <java-symbol type="string" name="log_access_confirmation_deny" />
+ <java-symbol type="string" name="log_access_confirmation_title" />
+ <java-symbol type="string" name="log_access_confirmation_body" />
+
<java-symbol type="string" name="config_defaultAssistantAccessComponent" />
<java-symbol type="string" name="slices_permission_request" />
@@ -4728,4 +4734,17 @@
<java-symbol type="string" name="vdm_camera_access_denied" />
<java-symbol type="color" name="camera_privacy_light"/>
+
+ <java-symbol type="bool" name="config_bg_current_drain_monitor_enabled" />
+ <java-symbol type="array" name="config_bg_current_drain_threshold_to_restricted_bucket" />
+ <java-symbol type="array" name="config_bg_current_drain_threshold_to_bg_restricted" />
+ <java-symbol type="integer" name="config_bg_current_drain_window" />
+ <java-symbol type="integer" name="config_bg_current_drain_types_to_restricted_bucket" />
+ <java-symbol type="integer" name="config_bg_current_drain_types_to_bg_restricted" />
+ <java-symbol type="integer" name="config_bg_current_drain_power_components" />
+ <java-symbol type="bool" name="config_bg_current_drain_event_duration_based_threshold_enabled" />
+ <java-symbol type="array" name="config_bg_current_drain_high_threshold_to_restricted_bucket" />
+ <java-symbol type="array" name="config_bg_current_drain_high_threshold_to_bg_restricted" />
+ <java-symbol type="integer" name="config_bg_current_drain_media_playback_min_duration" />
+ <java-symbol type="integer" name="config_bg_current_drain_location_min_duration" />
</resources>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 8f368c2..d35ecbd 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -924,6 +924,7 @@
* Checks if an activity is embedded and its presentation is customized by a
* {@link android.window.TaskFragmentOrganizer} to only occupy a portion of Task bounds.
*/
+ @Override
public boolean isActivityEmbedded(@NonNull Activity activity) {
return mPresenter.isActivityEmbedded(activity.getActivityToken());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 1039e2a..51067a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -191,6 +191,19 @@
return null;
}
+ /**
+ * Gets a token associated with the view that can be used to grant the view focus.
+ */
+ public IBinder getFocusGrantToken(View view) {
+ SurfaceControlViewHost root = mViewRoots.get(view);
+ if (root == null) {
+ Slog.e(TAG, "Couldn't get focus grant token since view does not exist in "
+ + "SystemWindow:" + view);
+ return null;
+ }
+ return root.getFocusGrantToken();
+ }
+
private class PerDisplay {
final int mDisplayId;
private final SparseArray<SysUiWindowManager> mWwms = new SparseArray<>();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 6ec8f5b..71cff02 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -28,7 +28,6 @@
import android.graphics.RectF;
import android.os.Debug;
import android.os.Handler;
-import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.util.Size;
@@ -126,7 +125,6 @@
private int mMenuState;
private PipMenuView mPipMenuView;
- private IBinder mPipMenuInputToken;
private ActionListener mMediaActionListener = new ActionListener() {
@Override
@@ -206,7 +204,6 @@
mApplier = null;
mSystemWindows.removeView(mPipMenuView);
mPipMenuView = null;
- mPipMenuInputToken = null;
}
/**
@@ -392,7 +389,6 @@
if (mApplier == null) {
mApplier = new SyncRtSurfaceTransactionApplier(mPipMenuView);
- mPipMenuInputToken = mPipMenuView.getViewRootImpl().getInputToken();
}
return mApplier != null;
@@ -539,7 +535,8 @@
try {
WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
- mPipMenuInputToken, menuState != MENU_STATE_NONE /* grantFocus */);
+ mSystemWindows.getFocusGrantToken(mPipMenuView),
+ menuState != MENU_STATE_NONE /* grantFocus */);
} catch (RemoteException e) {
Log.e(TAG, "Unable to update focus as menu appears/disappears", e);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index 32861b6..f838a0b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -143,6 +143,7 @@
mSystemWindows.addView(mPipMenuView,
getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
0, SHELL_ROOT_LAYER_PIP);
+ mPipMenuView.setFocusGrantToken(mSystemWindows.getFocusGrantToken(mPipMenuView));
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 0141b6a..84eae9e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -28,6 +28,7 @@
import android.app.RemoteAction;
import android.content.Context;
import android.os.Handler;
+import android.os.IBinder;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
@@ -69,6 +70,7 @@
private final ImageView mArrowRight;
private final ImageView mArrowDown;
private final ImageView mArrowLeft;
+ private IBinder mFocusGrantToken = null;
public TvPipMenuView(@NonNull Context context) {
this(context, null);
@@ -108,6 +110,10 @@
mListener = listener;
}
+ void setFocusGrantToken(IBinder token) {
+ mFocusGrantToken = token;
+ }
+
void show(boolean inMoveMode, int gravity) {
if (DEBUG) Log.d(TAG, "show(), inMoveMode: " + inMoveMode);
grantWindowFocus(true);
@@ -162,7 +168,7 @@
try {
WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
- getViewRootImpl().getInputToken(), grantFocus);
+ mFocusGrantToken, grantFocus);
} catch (Exception e) {
Log.e(TAG, "Unable to update focus", e);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 219530b..029d073 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -163,6 +163,7 @@
// and exit, since exit itself can trigger a number of changes that update the stages.
private boolean mShouldUpdateRecents;
private boolean mExitSplitScreenOnHide;
+ private boolean mIsDividerRemoteAnimating;
/** The target stage to dismiss to when unlock after folded. */
@StageType
@@ -389,6 +390,7 @@
RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps,
final IRemoteAnimationFinishedCallback finishedCallback) {
+ mIsDividerRemoteAnimating = true;
RemoteAnimationTarget[] augmentedNonApps =
new RemoteAnimationTarget[nonApps.length + 1];
for (int i = 0; i < nonApps.length; ++i) {
@@ -400,6 +402,7 @@
new IRemoteAnimationFinishedCallback.Stub() {
@Override
public void onAnimationFinished() throws RemoteException {
+ mIsDividerRemoteAnimating = false;
mShouldUpdateRecents = true;
mSyncQueue.queue(evictWct);
mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
@@ -423,6 +426,7 @@
@Override
public void onAnimationCancelled() {
+ mIsDividerRemoteAnimating = false;
mShouldUpdateRecents = true;
mSyncQueue.queue(evictWct);
mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
@@ -467,6 +471,7 @@
// Using legacy transitions, so we can't use blast sync since it conflicts.
mTaskOrganizer.applyTransaction(wct);
+ mSyncQueue.runInSync(t -> updateSurfaceBounds(mSplitLayout, t));
}
/** Start an intent and a task ordered by {@code intentFirst}. */
@@ -1055,7 +1060,7 @@
private void applyDividerVisibility(SurfaceControl.Transaction t) {
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
- if (dividerLeash == null) return;
+ if (mIsDividerRemoteAnimating || dividerLeash == null) return;
if (mDividerVisible) {
t.show(dividerLeash);
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
index 4ebaf2b..a48f94b 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
@@ -20,6 +20,7 @@
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -86,6 +87,7 @@
final int mType;
final int mRatType;
+ final int mSubId;
final String mSubscriberId;
final String mWifiNetworkKey;
final boolean mRoaming;
@@ -96,7 +98,7 @@
/** @hide */
public NetworkIdentity(
int type, int ratType, @Nullable String subscriberId, @Nullable String wifiNetworkKey,
- boolean roaming, boolean metered, boolean defaultNetwork, int oemManaged) {
+ boolean roaming, boolean metered, boolean defaultNetwork, int oemManaged, int subId) {
mType = type;
mRatType = ratType;
mSubscriberId = subscriberId;
@@ -105,12 +107,13 @@
mMetered = metered;
mDefaultNetwork = defaultNetwork;
mOemManaged = oemManaged;
+ mSubId = subId;
}
@Override
public int hashCode() {
return Objects.hash(mType, mRatType, mSubscriberId, mWifiNetworkKey, mRoaming, mMetered,
- mDefaultNetwork, mOemManaged);
+ mDefaultNetwork, mOemManaged, mSubId);
}
@Override
@@ -122,7 +125,8 @@
&& Objects.equals(mWifiNetworkKey, ident.mWifiNetworkKey)
&& mMetered == ident.mMetered
&& mDefaultNetwork == ident.mDefaultNetwork
- && mOemManaged == ident.mOemManaged;
+ && mOemManaged == ident.mOemManaged
+ && mSubId == ident.mSubId;
}
return false;
}
@@ -150,6 +154,7 @@
builder.append(", metered=").append(mMetered);
builder.append(", defaultNetwork=").append(mDefaultNetwork);
builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
+ builder.append(", subId=").append(mSubId);
return builder.append("}").toString();
}
@@ -256,6 +261,11 @@
return mOemManaged;
}
+ /** Get the SubId of this instance. */
+ public int getSubId() {
+ return mSubId;
+ }
+
/**
* Assemble a {@link NetworkIdentity} from the passed arguments.
*
@@ -276,7 +286,8 @@
public static NetworkIdentity buildNetworkIdentity(Context context,
@NonNull NetworkStateSnapshot snapshot, boolean defaultNetwork, int ratType) {
final NetworkIdentity.Builder builder = new NetworkIdentity.Builder()
- .setNetworkStateSnapshot(snapshot).setDefaultNetwork(defaultNetwork);
+ .setNetworkStateSnapshot(snapshot).setDefaultNetwork(defaultNetwork)
+ .setSubId(snapshot.getSubId());
if (snapshot.getLegacyType() == TYPE_MOBILE && ratType != NETWORK_TYPE_ALL) {
builder.setRatType(ratType);
}
@@ -325,6 +336,9 @@
if (res == 0) {
res = Integer.compare(left.mOemManaged, right.mOemManaged);
}
+ if (res == 0) {
+ res = Integer.compare(left.mSubId, right.mSubId);
+ }
return res;
}
@@ -345,6 +359,7 @@
private boolean mMetered;
private boolean mDefaultNetwork;
private int mOemManaged;
+ private int mSubId;
/**
* Creates a new Builder.
@@ -359,6 +374,7 @@
mMetered = false;
mDefaultNetwork = false;
mOemManaged = NetworkTemplate.OEM_MANAGED_NO;
+ mSubId = INVALID_SUBSCRIPTION_ID;
}
/**
@@ -537,6 +553,19 @@
return this;
}
+ /**
+ * Set the Subscription Id.
+ *
+ * @param subId the Subscription Id of the network. Or INVALID_SUBSCRIPTION_ID if not
+ * applicable.
+ * @return this builder.
+ */
+ @NonNull
+ public Builder setSubId(int subId) {
+ mSubId = subId;
+ return this;
+ }
+
private void ensureValidParameters() {
// Assert non-mobile network cannot have a ratType.
if (mType != TYPE_MOBILE && mRatType != NetworkTemplate.NETWORK_TYPE_ALL) {
@@ -559,7 +588,7 @@
public NetworkIdentity build() {
ensureValidParameters();
return new NetworkIdentity(mType, mRatType, mSubscriberId, mWifiNetworkKey,
- mRoaming, mMetered, mDefaultNetwork, mOemManaged);
+ mRoaming, mMetered, mDefaultNetwork, mOemManaged, mSubId);
}
}
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
index 2236d70..56461ba 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
@@ -17,6 +17,7 @@
package android.net;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import android.annotation.NonNull;
import android.service.NetworkIdentitySetProto;
@@ -42,6 +43,7 @@
private static final int VERSION_ADD_METERED = 4;
private static final int VERSION_ADD_DEFAULT_NETWORK = 5;
private static final int VERSION_ADD_OEM_MANAGED_NETWORK = 6;
+ private static final int VERSION_ADD_SUB_ID = 7;
/**
* Construct a {@link NetworkIdentitySet} object.
@@ -103,8 +105,15 @@
oemNetCapabilities = NetworkIdentity.OEM_NONE;
}
+ final int subId;
+ if (version >= VERSION_ADD_SUB_ID) {
+ subId = in.readInt();
+ } else {
+ subId = INVALID_SUBSCRIPTION_ID;
+ }
+
add(new NetworkIdentity(type, ratType, subscriberId, networkId, roaming, metered,
- defaultNetwork, oemNetCapabilities));
+ defaultNetwork, oemNetCapabilities, subId));
}
}
@@ -113,7 +122,7 @@
* @hide
*/
public void writeToStream(DataOutput out) throws IOException {
- out.writeInt(VERSION_ADD_OEM_MANAGED_NETWORK);
+ out.writeInt(VERSION_ADD_SUB_ID);
out.writeInt(size());
for (NetworkIdentity ident : this) {
out.writeInt(ident.getType());
@@ -124,6 +133,7 @@
out.writeBoolean(ident.isMetered());
out.writeBoolean(ident.isDefaultNetwork());
out.writeInt(ident.getOemManaged());
+ out.writeInt(ident.getSubId());
}
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java
index d577aa8..c018e91 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java
@@ -17,6 +17,8 @@
package android.net;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -98,12 +100,29 @@
return mLinkProperties;
}
- /** Get the Subscriber Id of the network associated with this snapshot. */
+ /**
+ * Get the Subscriber Id of the network associated with this snapshot.
+ * @deprecated Please use #getSubId, which doesn't return personally identifiable
+ * information.
+ */
+ @Deprecated
@Nullable
public String getSubscriberId() {
return mSubscriberId;
}
+ /** Get the subId of the network associated with this snapshot. */
+ public int getSubId() {
+ if (mNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+ final NetworkSpecifier spec = mNetworkCapabilities.getNetworkSpecifier();
+ if (spec instanceof TelephonyNetworkSpecifier) {
+ return ((TelephonyNetworkSpecifier) spec).getSubscriptionId();
+ }
+ }
+ return INVALID_SUBSCRIPTION_ID;
+ }
+
+
/**
* Get the legacy type of the network associated with this snapshot.
* @return the legacy network type. See {@code ConnectivityManager#TYPE_*}.
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index 78ba218..e8b3d4c 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -1484,10 +1484,15 @@
NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.isMetered()) {
// Copy the identify from IMS one but mark it as metered.
- NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
- ident.getRatType(), ident.getSubscriberId(), ident.getWifiNetworkKey(),
- ident.isRoaming(), true /* metered */,
- true /* onDefaultNetwork */, ident.getOemManaged());
+ NetworkIdentity vtIdent = new NetworkIdentity.Builder()
+ .setType(ident.getType())
+ .setRatType(ident.getRatType())
+ .setSubscriberId(ident.getSubscriberId())
+ .setWifiNetworkKey(ident.getWifiNetworkKey())
+ .setRoaming(ident.isRoaming()).setMetered(true)
+ .setDefaultNetwork(true)
+ .setOemManaged(ident.getOemManaged())
+ .setSubId(ident.getSubId()).build();
final String ifaceVt = IFACE_VT + getSubIdForMobile(snapshot);
findOrCreateNetworkIdentitySet(mActiveIfaces, ifaceVt).add(vtIdent);
findOrCreateNetworkIdentitySet(mActiveUidIfaces, ifaceVt).add(vtIdent);
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index 0102a97..4e06ed7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -448,21 +448,26 @@
uiContext.startActivity(intent);
}
- public void preview(DreamInfo dreamInfo) {
- logd("preview(%s)", dreamInfo);
- if (mDreamManager == null || dreamInfo == null || dreamInfo.componentName == null)
+ /**
+ * Preview a dream, given the component name.
+ */
+ public void preview(ComponentName componentName) {
+ logd("preview(%s)", componentName);
+ if (mDreamManager == null || componentName == null) {
return;
+ }
try {
- mDreamManager.testDream(mContext.getUserId(), dreamInfo.componentName);
+ mDreamManager.testDream(mContext.getUserId(), componentName);
} catch (RemoteException e) {
- Log.w(TAG, "Failed to preview " + dreamInfo, e);
+ Log.w(TAG, "Failed to preview " + componentName, e);
}
}
public void startDreaming() {
logd("startDreaming()");
- if (mDreamManager == null)
+ if (mDreamManager == null) {
return;
+ }
try {
mDreamManager.dream();
} catch (RemoteException e) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
index 484ca93..1f4cafce 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
@@ -221,8 +221,8 @@
setSwitchEnabled(false);
} else if (!mIsAllowedByOrganization) {
EnforcedAdmin admin =
- RestrictedLockUtilsInternal.checkIfInputMethodDisallowed(getContext(),
- mImi.getPackageName(), UserHandle.myUserId());
+ RestrictedLockUtilsInternal.checkIfInputMethodDisallowed(
+ getContext(), mImi.getPackageName(), mUserId);
setDisabledByAdmin(admin);
} else {
setEnabled(true);
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
index b4c95e6..2c2be03 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
@@ -39,6 +39,8 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
import java.util.zip.GZIPInputStream;
/**
@@ -54,6 +56,7 @@
private static final String TAG_FILE_NAME = "file-name";
private static final String TAG_FILE_CONTENT = "file-content";
private static final String ATTR_CONTENT_ID = "contentId";
+ private static final String ATTR_LIBRARY_NAME = "lib";
private static final String HTML_HEAD_STRING =
"<html><head>\n"
@@ -67,8 +70,12 @@
+ "</style>\n"
+ "</head>"
+ "<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\n"
- + "<div class=\"toc\">\n"
- + "<ul>";
+ + "<div class=\"toc\">\n";
+ private static final String LIBRARY_HEAD_STRING =
+ "<strong>Libraries</strong>\n<ul class=\"libraries\">";
+ private static final String LIBRARY_TAIL_STRING = "</ul>\n<strong>Files</strong>";
+
+ private static final String FILES_HEAD_STRING = "<ul class=\"files\">";
private static final String HTML_MIDDLE_STRING =
"</ul>\n"
@@ -81,12 +88,14 @@
private final List<File> mXmlFiles;
/*
- * A map from a file name to a content id (MD5 sum of file content) for its license.
- * For example, "/system/priv-app/TeleService/TeleService.apk" maps to
+ * A map from a file name to a library name (may be empty) to a content id (MD5 sum of file
+ * content) for its license.
+ * For example, "/system/priv-app/TeleService/TeleService.apk" maps to "service/Telephony" to
* "9645f39e9db895a4aa6e02cb57294595". Here "9645f39e9db895a4aa6e02cb57294595" is a MD5 sum
* of the content of packages/services/Telephony/MODULE_LICENSE_APACHE2.
*/
- private final Map<String, Set<String>> mFileNameToContentIdMap = new HashMap();
+ private final Map<String, Map<String, Set<String>>> mFileNameToLibraryToContentIdMap =
+ new HashMap();
/*
* A map from a content id (MD5 sum of file content) to a license file content.
@@ -98,7 +107,7 @@
static class ContentIdAndFileNames {
final String mContentId;
- final List<String> mFileNameList = new ArrayList();
+ final Map<String, List<String>> mLibraryToFileNameMap = new TreeMap();
ContentIdAndFileNames(String contentId) {
mContentId = contentId;
@@ -120,7 +129,7 @@
parse(xmlFile);
}
- if (mFileNameToContentIdMap.isEmpty() || mContentIdToFileContentMap.isEmpty()) {
+ if (mFileNameToLibraryToContentIdMap.isEmpty() || mContentIdToFileContentMap.isEmpty()) {
return false;
}
@@ -128,7 +137,7 @@
try {
writer = new PrintWriter(outputFile);
- generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer,
+ generateHtml(mFileNameToLibraryToContentIdMap, mContentIdToFileContentMap, writer,
noticeHeader);
writer.flush();
@@ -157,7 +166,7 @@
in = new FileReader(xmlFile);
}
- parse(in, mFileNameToContentIdMap, mContentIdToFileContentMap);
+ parse(in, mFileNameToLibraryToContentIdMap, mContentIdToFileContentMap);
in.close();
} catch (XmlPullParserException | IOException e) {
@@ -180,7 +189,8 @@
*
* <licenses>
* <file-name contentId="content_id_of_license1">file1</file-name>
- * <file-name contentId="content_id_of_license2">file2</file-name>
+ * <file-name contentId="content_id_of_license2" lib="name of library">file2</file-name>
+ * <file-name contentId="content_id_of_license2" lib="another library">file2</file-name>
* ...
* <file-content contentId="content_id_of_license1">license1 file contents</file-content>
* <file-content contentId="content_id_of_license2">license2 file contents</file-content>
@@ -188,10 +198,12 @@
* </licenses>
*/
@VisibleForTesting
- static void parse(InputStreamReader in, Map<String, Set<String>> outFileNameToContentIdMap,
+ static void parse(InputStreamReader in,
+ Map<String, Map<String, Set<String>>> outFileNameToLibraryToContentIdMap,
Map<String, String> outContentIdToFileContentMap)
throws XmlPullParserException, IOException {
- Map<String, Set<String>> fileNameToContentIdMap = new HashMap<String, Set<String>>();
+ Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap =
+ new HashMap<String, Map<String, Set<String>>>();
Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
XmlPullParser parser = Xml.newPullParser();
@@ -205,12 +217,15 @@
if (state == XmlPullParser.START_TAG) {
if (TAG_FILE_NAME.equals(parser.getName())) {
String contentId = parser.getAttributeValue("", ATTR_CONTENT_ID);
+ String libraryName = parser.getAttributeValue("", ATTR_LIBRARY_NAME);
if (!TextUtils.isEmpty(contentId)) {
String fileName = readText(parser).trim();
if (!TextUtils.isEmpty(fileName)) {
- Set<String> contentIds =
- fileNameToContentIdMap.computeIfAbsent(
- fileName, k -> new HashSet<>());
+ Map<String, Set<String>> libs =
+ fileNameToLibraryToContentIdMap.computeIfAbsent(
+ fileName, k -> new HashMap<>());
+ Set<String> contentIds = libs.computeIfAbsent(
+ libraryName, k -> new HashSet<>());
contentIds.add(contentId);
}
}
@@ -229,11 +244,17 @@
state = parser.next();
}
- for (Map.Entry<String, Set<String>> entry : fileNameToContentIdMap.entrySet()) {
- outFileNameToContentIdMap.merge(
- entry.getKey(), entry.getValue(), (s1, s2) -> {
- s1.addAll(s2);
- return s1;
+ for (Map.Entry<String, Map<String, Set<String>>> mapEntry :
+ fileNameToLibraryToContentIdMap.entrySet()) {
+ outFileNameToLibraryToContentIdMap.merge(
+ mapEntry.getKey(), mapEntry.getValue(), (m1, m2) -> {
+ for (Map.Entry<String, Set<String>> entry : m2.entrySet()) {
+ m1.merge(entry.getKey(), entry.getValue(), (s1, s2) -> {
+ s1.addAll(s2);
+ return s1;
+ });
+ }
+ return m1;
});
}
outContentIdToFileContentMap.putAll(contentIdToFileContentMap);
@@ -251,13 +272,28 @@
}
@VisibleForTesting
- static void generateHtml(Map<String, Set<String>> fileNameToContentIdMap,
+ static void generateHtml(Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap,
Map<String, String> contentIdToFileContentMap, PrintWriter writer,
String noticeHeader) {
List<String> fileNameList = new ArrayList();
- fileNameList.addAll(fileNameToContentIdMap.keySet());
+ fileNameList.addAll(fileNameToLibraryToContentIdMap.keySet());
Collections.sort(fileNameList);
+ SortedMap<String, Set<String>> libraryToContentIdMap = new TreeMap();
+ for (Map<String, Set<String>> libraryToContentValue :
+ fileNameToLibraryToContentIdMap.values()) {
+ for (Map.Entry<String, Set<String>> entry : libraryToContentValue.entrySet()) {
+ if (TextUtils.isEmpty(entry.getKey())) {
+ continue;
+ }
+ libraryToContentIdMap.merge(
+ entry.getKey(), entry.getValue(), (s1, s2) -> {
+ s1.addAll(s2);
+ return s1;
+ });
+ }
+ }
+
writer.println(HTML_HEAD_STRING);
if (!TextUtils.isEmpty(noticeHeader)) {
@@ -268,21 +304,56 @@
Map<String, Integer> contentIdToOrderMap = new HashMap();
List<ContentIdAndFileNames> contentIdAndFileNamesList = new ArrayList();
+ if (!libraryToContentIdMap.isEmpty()) {
+ writer.println(LIBRARY_HEAD_STRING);
+ for (Map.Entry<String, Set<String>> entry: libraryToContentIdMap.entrySet()) {
+ String libraryName = entry.getKey();
+ for (String contentId : entry.getValue()) {
+ // Assigns an id to a newly referred license file content.
+ if (!contentIdToOrderMap.containsKey(contentId)) {
+ contentIdToOrderMap.put(contentId, count);
+
+ // An index in contentIdAndFileNamesList is the order of each element.
+ contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
+ count++;
+ }
+ int id = contentIdToOrderMap.get(contentId);
+ writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, libraryName);
+ }
+ }
+ writer.println(LIBRARY_TAIL_STRING);
+ }
+
// Prints all the file list with a link to its license file content.
for (String fileName : fileNameList) {
- for (String contentId : fileNameToContentIdMap.get(fileName)) {
- // Assigns an id to a newly referred license file content.
- if (!contentIdToOrderMap.containsKey(contentId)) {
- contentIdToOrderMap.put(contentId, count);
-
- // An index in contentIdAndFileNamesList is the order of each element.
- contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
- count++;
+ for (Map.Entry<String, Set<String>> libToContentId :
+ fileNameToLibraryToContentIdMap.get(fileName).entrySet()) {
+ String libraryName = libToContentId.getKey();
+ if (libraryName == null) {
+ libraryName = "";
}
+ for (String contentId : libToContentId.getValue()) {
+ // Assigns an id to a newly referred license file content.
+ if (!contentIdToOrderMap.containsKey(contentId)) {
+ contentIdToOrderMap.put(contentId, count);
- int id = contentIdToOrderMap.get(contentId);
- contentIdAndFileNamesList.get(id).mFileNameList.add(fileName);
- writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, fileName);
+ // An index in contentIdAndFileNamesList is the order of each element.
+ contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
+ count++;
+ }
+
+ int id = contentIdToOrderMap.get(contentId);
+ ContentIdAndFileNames elem = contentIdAndFileNamesList.get(id);
+ List<String> files = elem.mLibraryToFileNameMap.computeIfAbsent(
+ libraryName, k -> new ArrayList());
+ files.add(fileName);
+ if (TextUtils.isEmpty(libraryName)) {
+ writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, fileName);
+ } else {
+ writer.format("<li><a href=\"#id%d\">%s - %s</a></li>\n",
+ id, fileName, libraryName);
+ }
+ }
}
}
@@ -292,19 +363,27 @@
// Prints all contents of the license files in order of id.
for (ContentIdAndFileNames contentIdAndFileNames : contentIdAndFileNamesList) {
writer.format("<tr id=\"id%d\"><td class=\"same-license\">\n", count);
- writer.println("<div class=\"label\">Notices for file(s):</div>");
- writer.println("<div class=\"file-list\">");
- for (String fileName : contentIdAndFileNames.mFileNameList) {
- writer.format("%s <br/>\n", fileName);
+ for (Map.Entry<String, List<String>> libraryFiles :
+ contentIdAndFileNames.mLibraryToFileNameMap.entrySet()) {
+ String libraryName = libraryFiles.getKey();
+ if (TextUtils.isEmpty(libraryName)) {
+ writer.println("<div class=\"label\">Notices for file(s):</div>");
+ } else {
+ writer.format("<div class=\"label\"><strong>%s</strong> used by:</div>\n",
+ libraryName);
+ }
+ writer.println("<div class=\"file-list\">");
+ for (String fileName : libraryFiles.getValue()) {
+ writer.format("%s <br/>\n", fileName);
+ }
+ writer.println("</div><!-- file-list -->");
+ count++;
}
- writer.println("</div><!-- file-list -->");
writer.println("<pre class=\"license-text\">");
writer.println(contentIdToFileContentMap.get(
contentIdAndFileNames.mContentId));
writer.println("</pre><!-- license-text -->");
writer.println("</td></tr><!-- same-license -->");
-
- count++;
}
writer.println(HTML_REAR_STRING);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 360361b..dd7db21 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -19,6 +19,7 @@
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
@@ -34,11 +35,13 @@
private static final String TAG = "BluetoothMediaDevice";
private CachedBluetoothDevice mCachedDevice;
+ private final AudioManager mAudioManager;
BluetoothMediaDevice(Context context, CachedBluetoothDevice device,
MediaRouter2Manager routerManager, MediaRoute2Info info, String packageName) {
super(context, routerManager, info, packageName);
mCachedDevice = device;
+ mAudioManager = context.getSystemService(AudioManager.class);
initDeviceRecord();
}
@@ -98,6 +101,12 @@
}
@Override
+ public boolean isMutingExpectedDevice() {
+ return mAudioManager.getMutingExpectedDevice() != null && mCachedDevice.getAddress().equals(
+ mAudioManager.getMutingExpectedDevice().getAddress());
+ }
+
+ @Override
public boolean isConnected() {
return mCachedDevice.getBondState() == BluetoothDevice.BOND_BONDED
&& mCachedDevice.isConnected();
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 970abff..c759962 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -320,6 +320,13 @@
}
if (mType == another.mType) {
+ // Check device is muting expected device
+ if (isMutingExpectedDevice()) {
+ return -1;
+ } else if (another.isMutingExpectedDevice()) {
+ return 1;
+ }
+
// Check fast pair device
if (isFastPairDevice()) {
return -1;
@@ -392,6 +399,14 @@
return false;
}
+ /**
+ * Check if it is muting expected device
+ * @return {@code true} if it is muting expected device, otherwise return {@code false}
+ */
+ protected boolean isMutingExpectedDevice() {
+ return false;
+ }
+
@Override
public boolean equals(Object obj) {
if (!(obj instanceof MediaDevice)) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
index e87461f..e348865 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
@@ -36,7 +36,7 @@
@RunWith(RobolectricTestRunner.class)
public class LicenseHtmlGeneratorFromXmlTest {
- private static final String VALILD_XML_STRING =
+ private static final String VALID_OLD_XML_STRING =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<licenses>\n"
+ "<file-name contentId=\"0\">/file0</file-name>\n"
@@ -44,7 +44,15 @@
+ "<file-content contentId=\"0\"><![CDATA[license content #0]]></file-content>\n"
+ "</licenses>";
- private static final String INVALILD_XML_STRING =
+ private static final String VALID_NEW_XML_STRING =
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<licenses>\n"
+ + "<file-name contentId=\"0\" lib=\"libA\">/file0</file-name>\n"
+ + "<file-name contentId=\"0\" lib=\"libB\">/file1</file-name>\n"
+ + "<file-content contentId=\"0\"><![CDATA[license content #0]]></file-content>\n"
+ + "</licenses>";
+
+ private static final String INVALID_XML_STRING =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<licenses2>\n"
+ "<file-name contentId=\"0\">/file0</file-name>\n"
@@ -64,13 +72,13 @@
+ "</style>\n"
+ "</head>"
+ "<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\n"
- + "<div class=\"toc\">\n"
- + "<ul>\n";
+ + "<div class=\"toc\">\n";
private static final String HTML_CUSTOM_HEADING = "Custom heading";
- private static final String HTML_BODY_STRING =
- "<li><a href=\"#id0\">/file0</a></li>\n"
+ private static final String HTML_OLD_BODY_STRING =
+ "<ul class=\"files\">\n"
+ + "<li><a href=\"#id0\">/file0</a></li>\n"
+ "<li><a href=\"#id1\">/file0</a></li>\n"
+ "<li><a href=\"#id0\">/file1</a></li>\n"
+ "</ul>\n"
@@ -97,66 +105,181 @@
+ "</td></tr><!-- same-license -->\n"
+ "</table></body></html>\n";
- private static final String EXPECTED_HTML_STRING = HTML_HEAD_STRING + HTML_BODY_STRING;
+ private static final String HTML_NEW_BODY_STRING =
+ "<strong>Libraries</strong>\n"
+ + "<ul class=\"libraries\">\n"
+ + "<li><a href=\"#id0\">libA</a></li>\n"
+ + "<li><a href=\"#id1\">libB</a></li>\n"
+ + "</ul>\n"
+ + "<strong>Files</strong>\n"
+ + "<ul class=\"files\">\n"
+ + "<li><a href=\"#id0\">/file0 - libA</a></li>\n"
+ + "<li><a href=\"#id1\">/file0 - libB</a></li>\n"
+ + "<li><a href=\"#id0\">/file1 - libA</a></li>\n"
+ + "</ul>\n"
+ + "</div><!-- table of contents -->\n"
+ + "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n"
+ + "<tr id=\"id0\"><td class=\"same-license\">\n"
+ + "<div class=\"label\">Notices for file(s):</div>\n"
+ + "<div class=\"file-list\">\n"
+ + "/file0 <br/>\n"
+ + "/file1 <br/>\n"
+ + "</div><!-- file-list -->\n"
+ + "<pre class=\"license-text\">\n"
+ + "license content #0\n"
+ + "</pre><!-- license-text -->\n"
+ + "</td></tr><!-- same-license -->\n"
+ + "<tr id=\"id1\"><td class=\"same-license\">\n"
+ + "<div class=\"label\">Notices for file(s):</div>\n"
+ + "<div class=\"file-list\">\n"
+ + "/file0 <br/>\n"
+ + "</div><!-- file-list -->\n"
+ + "<pre class=\"license-text\">\n"
+ + "license content #1\n"
+ + "</pre><!-- license-text -->\n"
+ + "</td></tr><!-- same-license -->\n"
+ + "</table></body></html>\n";
- private static final String EXPECTED_HTML_STRING_WITH_CUSTOM_HEADING =
- HTML_HEAD_STRING + HTML_CUSTOM_HEADING + "\n" + HTML_BODY_STRING;
+ private static final String EXPECTED_OLD_HTML_STRING = HTML_HEAD_STRING + HTML_OLD_BODY_STRING;
+
+ private static final String EXPECTED_NEW_HTML_STRING = HTML_HEAD_STRING + HTML_NEW_BODY_STRING;
+
+ private static final String EXPECTED_OLD_HTML_STRING_WITH_CUSTOM_HEADING =
+ HTML_HEAD_STRING + HTML_CUSTOM_HEADING + "\n" + HTML_OLD_BODY_STRING;
+
+ private static final String EXPECTED_NEW_HTML_STRING_WITH_CUSTOM_HEADING =
+ HTML_HEAD_STRING + HTML_CUSTOM_HEADING + "\n" + HTML_NEW_BODY_STRING;
@Test
public void testParseValidXmlStream() throws XmlPullParserException, IOException {
- Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
+ Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
Map<String, String> contentIdToFileContentMap = new HashMap<>();
LicenseHtmlGeneratorFromXml.parse(
- new InputStreamReader(new ByteArrayInputStream(VALILD_XML_STRING.getBytes())),
- fileNameToContentIdMap, contentIdToFileContentMap);
- assertThat(fileNameToContentIdMap.size()).isEqualTo(2);
- assertThat(fileNameToContentIdMap.get("/file0")).containsExactly("0");
- assertThat(fileNameToContentIdMap.get("/file1")).containsExactly("0");
+ new InputStreamReader(new ByteArrayInputStream(VALID_OLD_XML_STRING.getBytes())),
+ fileNameToLibraryToContentIdMap, contentIdToFileContentMap);
+ assertThat(fileNameToLibraryToContentIdMap.size()).isEqualTo(1);
+ assertThat(fileNameToLibraryToContentIdMap.get("").size()).isEqualTo(2);
+ assertThat(fileNameToLibraryToContentIdMap.get("").get("/file0")).containsExactly("0");
+ assertThat(fileNameToLibraryToContentIdMap.get("").get("/file1")).containsExactly("0");
+ assertThat(contentIdToFileContentMap.size()).isEqualTo(1);
+ assertThat(contentIdToFileContentMap.get("0")).isEqualTo("license content #0");
+ }
+
+ @Test
+ public void testParseNewValidXmlStream() throws XmlPullParserException, IOException {
+ Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<>();
+
+ LicenseHtmlGeneratorFromXml.parse(
+ new InputStreamReader(new ByteArrayInputStream(VALID_NEW_XML_STRING.getBytes())),
+ fileNameToLibraryToContentIdMap, contentIdToFileContentMap);
+ assertThat(fileNameToLibraryToContentIdMap.size()).isEqualTo(2);
+ assertThat(fileNameToLibraryToContentIdMap.get("libA").size()).isEqualTo(1);
+ assertThat(fileNameToLibraryToContentIdMap.get("libB").size()).isEqualTo(1);
+ assertThat(fileNameToLibraryToContentIdMap.get("libA").get("/file0")).containsExactly("0");
+ assertThat(fileNameToLibraryToContentIdMap.get("libB").get("/file1")).containsExactly("0");
assertThat(contentIdToFileContentMap.size()).isEqualTo(1);
assertThat(contentIdToFileContentMap.get("0")).isEqualTo("license content #0");
}
@Test(expected = XmlPullParserException.class)
public void testParseInvalidXmlStream() throws XmlPullParserException, IOException {
- Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
+ Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
Map<String, String> contentIdToFileContentMap = new HashMap<>();
LicenseHtmlGeneratorFromXml.parse(
- new InputStreamReader(new ByteArrayInputStream(INVALILD_XML_STRING.getBytes())),
- fileNameToContentIdMap, contentIdToFileContentMap);
+ new InputStreamReader(new ByteArrayInputStream(INVALID_XML_STRING.getBytes())),
+ fileNameToLibraryToContentIdMap, contentIdToFileContentMap);
}
@Test
public void testGenerateHtml() {
- Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
+ Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
Map<String, String> contentIdToFileContentMap = new HashMap<>();
+ Map<String, Set<String>> toBoth = new HashMap<>();
+ Map<String, Set<String>> toOne = new HashMap<>();
- fileNameToContentIdMap.put("/file0", new HashSet<String>(Arrays.asList("0", "1")));
- fileNameToContentIdMap.put("/file1", new HashSet<String>(Arrays.asList("0")));
+ toBoth.put("", new HashSet<String>(Arrays.asList("0", "1")));
+ toOne.put("", new HashSet<String>(Arrays.asList("0")));
+
+ fileNameToLibraryToContentIdMap.put("/file0", toBoth);
+ fileNameToLibraryToContentIdMap.put("/file1", toOne);
contentIdToFileContentMap.put("0", "license content #0");
contentIdToFileContentMap.put("1", "license content #1");
StringWriter output = new StringWriter();
LicenseHtmlGeneratorFromXml.generateHtml(
- fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output), "");
- assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING);
+ fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
+ new PrintWriter(output), "");
+ assertThat(output.toString()).isEqualTo(EXPECTED_OLD_HTML_STRING);
+ }
+
+ @Test
+ public void testGenerateNewHtml() {
+ Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<>();
+ Map<String, Set<String>> toBoth = new HashMap<>();
+ Map<String, Set<String>> toOne = new HashMap<>();
+
+ toBoth.put("libA", new HashSet<String>(Arrays.asList("0")));
+ toBoth.put("libB", new HashSet<String>(Arrays.asList("1")));
+ toOne.put("libA", new HashSet<String>(Arrays.asList("0")));
+
+ fileNameToLibraryToContentIdMap.put("/file0", toBoth);
+ fileNameToLibraryToContentIdMap.put("/file1", toOne);
+ contentIdToFileContentMap.put("0", "license content #0");
+ contentIdToFileContentMap.put("1", "license content #1");
+
+ StringWriter output = new StringWriter();
+ LicenseHtmlGeneratorFromXml.generateHtml(
+ fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
+ new PrintWriter(output), "");
+ assertThat(output.toString()).isEqualTo(EXPECTED_NEW_HTML_STRING);
}
@Test
public void testGenerateHtmlWithCustomHeading() {
- Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
+ Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
Map<String, String> contentIdToFileContentMap = new HashMap<>();
+ Map<String, Set<String>> toBoth = new HashMap<>();
+ Map<String, Set<String>> toOne = new HashMap<>();
- fileNameToContentIdMap.put("/file0", new HashSet<String>(Arrays.asList("0", "1")));
- fileNameToContentIdMap.put("/file1", new HashSet<String>(Arrays.asList("0")));
+ toBoth.put("", new HashSet<String>(Arrays.asList("0", "1")));
+ toOne.put("", new HashSet<String>(Arrays.asList("0")));
+
+ fileNameToLibraryToContentIdMap.put("/file0", toBoth);
+ fileNameToLibraryToContentIdMap.put("/file1", toOne);
contentIdToFileContentMap.put("0", "license content #0");
contentIdToFileContentMap.put("1", "license content #1");
StringWriter output = new StringWriter();
LicenseHtmlGeneratorFromXml.generateHtml(
- fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output),
- HTML_CUSTOM_HEADING);
- assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING_WITH_CUSTOM_HEADING);
+ fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
+ new PrintWriter(output), HTML_CUSTOM_HEADING);
+ assertThat(output.toString()).isEqualTo(EXPECTED_OLD_HTML_STRING_WITH_CUSTOM_HEADING);
+ }
+
+ @Test
+ public void testGenerateNewHtmlWithCustomHeading() {
+ Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<>();
+ Map<String, Set<String>> toBoth = new HashMap<>();
+ Map<String, Set<String>> toOne = new HashMap<>();
+
+ toBoth.put("libA", new HashSet<String>(Arrays.asList("0")));
+ toBoth.put("libB", new HashSet<String>(Arrays.asList("1")));
+ toOne.put("libA", new HashSet<String>(Arrays.asList("0")));
+
+ fileNameToLibraryToContentIdMap.put("/file0", toBoth);
+ fileNameToLibraryToContentIdMap.put("/file1", toOne);
+ contentIdToFileContentMap.put("0", "license content #0");
+ contentIdToFileContentMap.put("1", "license content #1");
+
+ StringWriter output = new StringWriter();
+ LicenseHtmlGeneratorFromXml.generateHtml(
+ fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
+ new PrintWriter(output), HTML_CUSTOM_HEADING);
+ assertThat(output.toString()).isEqualTo(EXPECTED_NEW_HTML_STRING_WITH_CUSTOM_HEADING);
}
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java
deleted file mode 100644
index 6d1408d..0000000
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2017 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.plugins.qs;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-
-@ProvidesInterface(version = DetailAdapter.VERSION)
-public interface DetailAdapter {
- public static final int VERSION = 1;
-
- CharSequence getTitle();
- Boolean getToggleState();
-
- default boolean getToggleEnabled() {
- return true;
- }
-
- View createDetailView(Context context, View convertView, ViewGroup parent);
-
- /**
- * @return intent for opening more settings related to this detail panel. If null, the more
- * settings button will not be shown
- */
- Intent getSettingsIntent();
-
- /**
- * @return resource id of the string to use for opening the settings intent. If
- * {@code Resources.ID_NULL}, then use the default string:
- * {@code com.android.systemui.R.string.quick_settings_more_settings}
- */
- default int getSettingsText() {
- return Resources.ID_NULL;
- }
-
- /**
- * @return resource id of the string to use for closing the detail panel. If
- * {@code Resources.ID_NULL}, then use the default string:
- * {@code com.android.systemui.R.string.quick_settings_done}
- */
- default int getDoneText() {
- return Resources.ID_NULL;
- }
-
- void setToggleState(boolean state);
- int getMetricsCategory();
-
- /**
- * Indicates whether the detail view wants to have its header (back button, title and
- * toggle) shown.
- */
- default boolean hasHeader() {
- return true;
- }
-
- /**
- * Indicates whether the detail view wants to animate when shown. This has no affect over the
- * closing animation. Detail panels will always animate when closed.
- */
- default boolean shouldAnimate() {
- return true;
- }
-
- /**
- * @return true if the callback handled the event and wants to keep the detail panel open, false
- * otherwise. Returning false will close the panel.
- */
- default boolean onDoneButtonClicked() {
- return false;
- }
-
- default UiEventLogger.UiEventEnum openDetailEvent() {
- return INVALID;
- }
-
- default UiEventLogger.UiEventEnum closeDetailEvent() {
- return INVALID;
- }
-
- default UiEventLogger.UiEventEnum moreSettingsEvent() {
- return INVALID;
- }
-
- UiEventLogger.UiEventEnum INVALID = () -> 0;
-}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index 1ef5324..669d6a3 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -35,14 +35,12 @@
@ProvidesInterface(version = QSTile.VERSION)
@DependsOn(target = QSIconView.class)
-@DependsOn(target = DetailAdapter.class)
@DependsOn(target = Callback.class)
@DependsOn(target = Icon.class)
@DependsOn(target = State.class)
public interface QSTile {
- int VERSION = 2;
+ int VERSION = 3;
- DetailAdapter getDetailAdapter();
String getTileSpec();
boolean isAvailable();
@@ -117,12 +115,9 @@
}
@ProvidesInterface(version = Callback.VERSION)
- public interface Callback {
- public static final int VERSION = 1;
+ interface Callback {
+ static final int VERSION = 2;
void onStateChanged(State state);
- void onShowDetail(boolean show);
- void onToggleStateChanged(boolean state);
- void onScanStateChanged(boolean state);
}
@ProvidesInterface(version = Icon.VERSION)
diff --git a/packages/SystemUI/res/color-night/qs_detail_progress_track.xml b/packages/SystemUI/res/color-night/qs_detail_progress_track.xml
deleted file mode 100644
index c56382e..0000000
--- a/packages/SystemUI/res/color-night/qs_detail_progress_track.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- I really don't want to define this, but the View that uses this asset uses both the
- light and dark accent colors. -->
- <item android:alpha="0.6" android:drawable="@*android:color/accent_device_default_light" />
-</selector>
diff --git a/packages/SystemUI/res/color/qs_detail_progress_track.xml b/packages/SystemUI/res/color/qs_detail_progress_track.xml
deleted file mode 100644
index d86119f..0000000
--- a/packages/SystemUI/res/color/qs_detail_progress_track.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- I really don't want to define this, but the View that uses this asset uses both the
- light and dark accent colors. -->
- <item android:alpha="0.6" android:drawable="@*android:color/accent_device_default_dark" />
-</selector>
diff --git a/packages/SystemUI/res/drawable/qs_detail_background.xml b/packages/SystemUI/res/drawable/qs_detail_background.xml
deleted file mode 100644
index c23649d..0000000
--- a/packages/SystemUI/res/drawable/qs_detail_background.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<!--
-Copyright (C) 2014 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.
--->
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
- <item>
- <inset>
- <shape>
- <solid android:color="@android:color/transparent"/>
- <corners android:radius="@dimen/qs_footer_action_corner_radius" />
- </shape>
- </inset>
- </item>
- <item>
- <inset>
- <shape>
- <solid android:color="?android:attr/colorBackgroundFloating"/>
- <corners android:radius="@dimen/qs_footer_action_corner_radius" />
- </shape>
- </inset>
- </item>
-</transition>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
deleted file mode 100644
index 78655c0..0000000
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 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.
--->
-<!-- Extends LinearLayout -->
-<com.android.systemui.qs.QSDetail
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/qs_detail_background"
- android:layout_marginTop="@dimen/qs_detail_margin_top"
- android:clickable="true"
- android:orientation="vertical"
- android:paddingBottom="8dp"
- android:visibility="invisible"
- android:elevation="4dp"
- android:importantForAccessibility="no" >
-
- <include
- android:id="@+id/qs_detail_header"
- layout="@layout/qs_detail_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- />
-
- <com.android.systemui.statusbar.AlphaOptimizedImageView
- android:id="@+id/qs_detail_header_progress"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:alpha="0"
- android:background="@color/qs_detail_progress_track"
- android:src="@drawable/indeterminate_anim"
- android:scaleType="fitXY"
- />
-
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:fillViewport="true">
-
- <FrameLayout
- android:id="@android:id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
- </ScrollView>
-
- <include layout="@layout/qs_detail_buttons" />
-
-</com.android.systemui.qs.QSDetail>
diff --git a/packages/SystemUI/res/layout/qs_detail_buttons.xml b/packages/SystemUI/res/layout/qs_detail_buttons.xml
deleted file mode 100644
index 75f43f9..0000000
--- a/packages/SystemUI/res/layout/qs_detail_buttons.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingEnd="8dp"
- android:gravity="end">
-
- <TextView
- android:id="@android:id/button2"
- style="@style/QSBorderlessButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="8dp"
- android:minHeight="48dp"
- android:minWidth="132dp"
- android:textAppearance="@style/TextAppearance.QS.DetailButton"
- android:focusable="true" />
-
- <TextView
- android:id="@android:id/button1"
- style="@style/QSBorderlessButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:minHeight="48dp"
- android:minWidth="88dp"
- android:textAppearance="@style/TextAppearance.QS.DetailButton"
- android:focusable="true"/>
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_detail_header.xml b/packages/SystemUI/res/layout/qs_detail_header.xml
deleted file mode 100644
index d1ab054..0000000
--- a/packages/SystemUI/res/layout/qs_detail_header.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 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.
--->
-<com.android.keyguard.AlphaOptimizedLinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="@dimen/qs_detail_header_padding"
- android:paddingTop="@dimen/qs_detail_header_padding"
- android:paddingBottom="@dimen/qs_detail_items_padding_top"
- android:paddingEnd="@dimen/qs_panel_padding"
- android:background="@drawable/btn_borderless_rect"
- android:orientation="vertical"
- android:gravity="center">
-
- <com.android.systemui.ResizingSpace
- android:layout_width="match_parent"
- android:layout_height="@dimen/qs_detail_header_margin_top" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <TextView
- android:id="@android:id/title"
- android:paddingStart="@dimen/qs_detail_header_text_padding"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:textDirection="locale"
- android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
-
- <ImageView
- android:id="@+id/settings"
- android:layout_width="@dimen/qs_detail_image_width"
- android:layout_height="@dimen/qs_detail_image_height"
- android:background="?android:attr/selectableItemBackground"
- android:padding="@dimen/qs_detail_image_padding"
- android:src="@drawable/ic_settings"
- android:visibility="gone"/>
-
- <ViewStub
- android:id="@+id/toggle_stub"
- android:inflatedId="@+id/toggle"
- android:layout="@layout/qs_detail_switch"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- </LinearLayout>
-
-</com.android.keyguard.AlphaOptimizedLinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_detail_item.xml b/packages/SystemUI/res/layout/qs_detail_item.xml
deleted file mode 100644
index 0844bb4..0000000
--- a/packages/SystemUI/res/layout/qs_detail_item.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="@dimen/qs_detail_item_height"
- android:background="@drawable/btn_borderless_rect"
- android:clickable="true"
- android:focusable="true"
- android:gravity="center_vertical"
- android:orientation="horizontal" >
-
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="@dimen/qs_detail_item_icon_width"
- android:layout_height="@dimen/qs_detail_item_icon_size"
- android:layout_marginStart="@dimen/qs_detail_item_icon_marginStart"
- android:layout_marginEnd="@dimen/qs_detail_item_icon_marginEnd"
- android:tint="?android:attr/textColorPrimary"/>
-
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="12dp"
- android:layout_weight="1"
- android:orientation="vertical" >
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textDirection="locale"
- android:ellipsize="end"
- android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
-
- <TextView
- android:id="@android:id/summary"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textDirection="locale"
- android:layout_marginTop="2dp"
- android:textAppearance="@style/TextAppearance.QS.DetailItemSecondary" />
- </LinearLayout>
-
- <ImageView
- android:id="@android:id/icon2"
- style="@style/QSBorderlessButton"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:clickable="true"
- android:focusable="true"
- android:scaleType="center"
- android:contentDescription="@*android:string/media_route_controller_disconnect"
- android:tint="?android:attr/textColorPrimary" />
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_detail_items.xml b/packages/SystemUI/res/layout/qs_detail_items.xml
deleted file mode 100644
index 60cba67..0000000
--- a/packages/SystemUI/res/layout/qs_detail_items.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 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.
--->
-<!-- extends FrameLayout -->
-<com.android.systemui.qs.QSDetailItems
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:sysui="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingStart="@dimen/qs_detail_padding_start"
- android:paddingEnd="16dp">
-
- <com.android.systemui.qs.AutoSizingList
- android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- sysui:itemHeight="@dimen/qs_detail_item_height"
- style="@style/AutoSizingList"/>
-
- <LinearLayout
- android:id="@android:id/empty"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:gravity="center"
- android:orientation="vertical">
-
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="56dp"
- android:layout_height="56dp"
- android:tint="?android:attr/textColorSecondary" />
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dp"
- android:textAppearance="@style/TextAppearance.QS.DetailEmpty"/>
- </LinearLayout>
-</com.android.systemui.qs.QSDetailItems>
diff --git a/packages/SystemUI/res/layout/qs_detail_switch.xml b/packages/SystemUI/res/layout/qs_detail_switch.xml
deleted file mode 100644
index abb2497..0000000
--- a/packages/SystemUI/res/layout/qs_detail_switch.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
- ~ Copyright (C) 2019 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.
- -->
-
-<Switch
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/toggle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:clickable="false"
- android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 85b33cc..2040051 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -47,10 +47,6 @@
<include layout="@layout/quick_status_bar_expanded_header" />
- <include
- android:id="@+id/qs_detail"
- layout="@layout/qs_detail" />
-
<ViewStub
android:id="@+id/container_stub"
android:inflatedId="@+id/qs_footer_actions"
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index 3a0df28..0c847ed 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -52,7 +52,7 @@
android:gravity="center_horizontal" />
<ImageView
android:id="@+id/restricted_padlock"
- android:layout_width="@dimen/qs_detail_item_secondary_text_size"
+ android:layout_width="@dimen/qs_tile_text_size"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:src="@drawable/ic_info"
diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index 4f4bae4..816dfd3 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -19,6 +19,7 @@
android:id="@+id/system_icons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
+ android:layout_gravity="center_vertical|end"
android:gravity="center_vertical">
<com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons"
diff --git a/packages/SystemUI/res/layout/tuner_zen_mode_panel.xml b/packages/SystemUI/res/layout/tuner_zen_mode_panel.xml
deleted file mode 100644
index efe63d7..0000000
--- a/packages/SystemUI/res/layout/tuner_zen_mode_panel.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2016 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.
--->
-<!-- extends LinearLayout -->
-<com.android.systemui.tuner.TunerZenModePanel
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/tuner_zen_mode_panel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:visibility="gone"
- android:orientation="vertical" >
-
- <View
- android:id="@+id/zen_embedded_divider"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:layout_marginBottom="12dp"
- android:layout_marginTop="8dp"
- android:background="@color/qs_tile_divider" />
-
- <include
- android:layout_width="match_parent"
- android:layout_height="48dp"
- android:layout_marginStart="16dp"
- android:id="@+id/tuner_zen_switch"
- layout="@layout/qs_detail_header" />
-
- <include layout="@layout/zen_mode_panel" />
-
- <include
- android:id="@+id/tuner_zen_buttons"
- layout="@layout/qs_detail_buttons" />
-
-</com.android.systemui.tuner.TunerZenModePanel>
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
deleted file mode 100644
index 5862413..0000000
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 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.
--->
-<!-- extends LinearLayout -->
-<com.android.systemui.volume.ZenModePanel xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/zen_mode_panel"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipChildren="false" >
-
- <LinearLayout
- android:id="@+id/edit_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="?android:attr/colorPrimary"
- android:clipChildren="false"
- android:orientation="vertical">
-
- <com.android.systemui.volume.SegmentedButtons
- android:id="@+id/zen_buttons"
- android:background="@drawable/segmented_buttons_background"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginBottom="8dp" />
-
- <RelativeLayout
- android:id="@+id/zen_introduction"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:background="@drawable/zen_introduction_message_background"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent.Light">
-
- <ImageView
- android:id="@+id/zen_introduction_confirm"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_marginEnd="8dp"
- android:layout_alignParentEnd="true"
- android:background="@drawable/btn_borderless_rect"
- android:clickable="true"
- android:contentDescription="@string/accessibility_desc_close"
- android:scaleType="center"
- android:src="@drawable/ic_close_white_rounded" />
-
- <TextView
- android:id="@+id/zen_introduction_message"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="12dp"
- android:layout_marginStart="24dp"
- android:textDirection="locale"
- android:lineSpacingMultiplier="1.20029"
- android:layout_toStartOf="@id/zen_introduction_confirm"
- android:textAppearance="@style/TextAppearance.QS.Introduction" />
-
- <TextView
- android:id="@+id/zen_introduction_customize"
- style="@style/QSBorderlessButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:layout_marginEnd="12dp"
- android:layout_below="@id/zen_introduction_message"
- android:clickable="true"
- android:focusable="true"
- android:text="@string/zen_priority_customize_button"
- android:textAppearance="@style/TextAppearance.QS.DetailButton.White" />
-
- <View
- android:layout_width="0dp"
- android:layout_height="16dp"
- android:layout_below="@id/zen_introduction_message"
- android:layout_alignParentEnd="true" />
-
- </RelativeLayout>
-
- <com.android.settingslib.notification.ZenRadioLayout
- android:id="@+id/zen_conditions"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:layout_marginEnd="4dp"
- android:layout_marginStart="4dp"
- android:paddingBottom="@dimen/zen_mode_condition_detail_bottom_padding"
- android:orientation="horizontal" >
- <RadioGroup
- android:id="@+id/zen_radio_buttons"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- <LinearLayout
- android:id="@+id/zen_radio_buttons_content"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"/>
- </com.android.settingslib.notification.ZenRadioLayout>
-
- <TextView
- android:id="@+id/zen_alarm_warning"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="18dp"
- android:layout_marginEnd="16dp"
- android:textDirection="locale"
- android:lineSpacingMultiplier="1.20029"
- android:textAppearance="@style/TextAppearance.QS.Warning" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@android:id/empty"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:background="?android:attr/colorPrimary"
- android:gravity="center"
- android:orientation="vertical">
-
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="56dp"
- android:layout_height="56dp"
- android:alpha="?android:attr/disabledAlpha"
- android:tint="?android:attr/colorForeground" />
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dp"
- android:textAppearance="@style/TextAppearance.QS.DetailEmpty"/>
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/auto_rule"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="?android:attr/colorPrimary"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="8dp"
- android:orientation="vertical">
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary"/>
-
- </LinearLayout>
-
-</com.android.systemui.volume.ZenModePanel>
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index 8c5006d..062e33c 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -33,4 +33,10 @@
<!-- Max number of columns for power menu -->
<integer name="power_menu_max_columns">4</integer>
+
+ <!-- Max number of columns for power menu lite -->
+ <integer name="power_menu_lite_max_columns">4</integer>
+ <!-- Max number of rows for power menu lite -->
+ <integer name="power_menu_lite_max_rows">2</integer>
+
</resources>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 9d24e9b..01eb09b 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -38,11 +38,6 @@
<dimen name="qs_footer_padding">14dp</dimen>
<dimen name="qs_security_footer_background_inset">12dp</dimen>
- <dimen name="battery_detail_graph_space_top">9dp</dimen>
- <dimen name="battery_detail_graph_space_bottom">9dp</dimen>
-
- <dimen name="qs_detail_header_margin_top">14dp</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-sw410dp/dimens.xml b/packages/SystemUI/res/values-sw410dp/dimens.xml
index d33ee99..7da47e5 100644
--- a/packages/SystemUI/res/values-sw410dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw410dp/dimens.xml
@@ -20,8 +20,6 @@
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. -->
<resources>
- <dimen name="qs_detail_items_padding_top">16dp</dimen>
-
<!-- Global actions grid -->
<dimen name="global_actions_grid_vertical_padding">8dp</dimen>
<dimen name="global_actions_grid_horizontal_padding">4dp</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index fe546f6..588638f 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -33,4 +33,7 @@
<!-- Notifications are sized to match the width of two (of 4) qs tiles in landscape. -->
<bool name="config_skinnyNotifsInLandscape">false</bool>
+ <integer name="power_menu_lite_max_columns">3</integer>
+ <integer name="power_menu_lite_max_rows">2</integer>
+
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-port/config.xml b/packages/SystemUI/res/values-sw600dp-port/config.xml
index 3c6a81e..857e162 100644
--- a/packages/SystemUI/res/values-sw600dp-port/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/config.xml
@@ -23,4 +23,8 @@
<!-- The number of columns in the QuickSettings -->
<integer name="quick_settings_num_columns">3</integer>
+
+ <integer name="power_menu_lite_max_columns">2</integer>
+ <integer name="power_menu_lite_max_rows">3</integer>
+
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index a66ed15..8f6bde5 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -65,9 +65,6 @@
<dimen name="qs_security_footer_single_line_height">48dp</dimen>
<dimen name="qs_security_footer_background_inset">0dp</dimen>
- <!-- When split shade is used, this panel should be aligned to the top -->
- <dimen name="qs_detail_margin_top">0dp</dimen>
-
<!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
<dimen name="large_dialog_width">472dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index e6ab0ff..2992859 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -114,13 +114,6 @@
<attr name="hybridNotificationTextStyle" format="reference" />
</declare-styleable>
- <declare-styleable name="AutoSizingList">
- <!-- Whether AutoSizingList will show only as many items as fit on screen and
- remove extra items instead of scrolling. -->
- <attr name="enableAutoSizing" format="boolean" />
- <attr name="itemHeight" format="dimension" />
- </declare-styleable>
-
<declare-styleable name="PluginInflateContainer">
<attr name="viewType" format="string" />
</declare-styleable>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index fe79f27..d1f4f19 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -500,26 +500,10 @@
<dimen name="qs_panel_elevation">4dp</dimen>
<dimen name="qs_panel_padding_bottom">@dimen/new_footer_height</dimen>
<dimen name="qs_panel_padding_top">48dp</dimen>
- <dimen name="qs_detail_header_padding">0dp</dimen>
- <dimen name="qs_detail_image_width">56dp</dimen>
- <dimen name="qs_detail_image_height">56dp</dimen>
- <dimen name="qs_detail_image_padding">16dp</dimen>
- <dimen name="qs_detail_item_height">48dp</dimen>
- <dimen name="qs_detail_header_text_size">20sp</dimen>
- <dimen name="qs_detail_button_text_size">14sp</dimen>
- <dimen name="qs_detail_item_primary_text_size">16sp</dimen>
- <dimen name="qs_detail_item_secondary_text_size">14sp</dimen>
- <dimen name="qs_detail_empty_text_size">14sp</dimen>
- <dimen name="qs_detail_header_margin_top">28dp</dimen>
- <dimen name="qs_detail_header_text_padding">16dp</dimen>
+
<dimen name="qs_data_usage_text_size">14sp</dimen>
<dimen name="qs_data_usage_usage_text_size">36sp</dimen>
- <dimen name="qs_detail_padding_start">16dp</dimen>
- <dimen name="qs_detail_items_padding_top">4dp</dimen>
- <dimen name="qs_detail_item_icon_size">24dp</dimen>
- <dimen name="qs_detail_item_icon_width">32dp</dimen>
- <dimen name="qs_detail_item_icon_marginStart">0dp</dimen>
- <dimen name="qs_detail_item_icon_marginEnd">20dp</dimen>
+
<dimen name="qs_header_mobile_icon_size">@dimen/status_bar_icon_drawing_size</dimen>
<dimen name="qs_header_carrier_separator_width">6dp</dimen>
<dimen name="qs_carrier_margin_width">4dp</dimen>
@@ -533,9 +517,6 @@
<dimen name="qs_security_footer_background_inset">0dp</dimen>
<dimen name="qs_security_footer_corner_radius">28dp</dimen>
- <!-- Desired qs icon overlay size. -->
- <dimen name="qs_detail_icon_overlay_size">24dp</dimen>
-
<dimen name="segmented_button_spacing">0dp</dimen>
<dimen name="borderless_button_radius">2dp</dimen>
@@ -547,8 +528,6 @@
<!-- Padding between subtitles and the following text in the QSFooter dialog -->
<dimen name="qs_footer_dialog_subtitle_padding">20dp</dimen>
- <dimen name="qs_detail_margin_top">@*android:dimen/quick_qs_offset_height</dimen>
-
<!-- Zen mode panel: spacing between two-line condition upper and lower lines -->
<dimen name="zen_mode_condition_detail_item_interline_spacing">4dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6a34ada..3b7e9d4 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -609,8 +609,6 @@
<string name="quick_settings_inversion_label">Color inversion</string>
<!-- QuickSettings: Label for the toggle that controls whether display color correction is enabled. [CHAR LIMIT=NONE] -->
<string name="quick_settings_color_correction_label">Color correction</string>
- <!-- QuickSettings: Control panel: Label for button that navigates to settings. [CHAR LIMIT=NONE] -->
- <string name="quick_settings_more_settings">More settings</string>
<!-- QuickSettings: Control panel: Label for button that navigates to user settings. [CHAR LIMIT=NONE] -->
<string name="quick_settings_more_user_settings">User settings</string>
<!-- QuickSettings: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] -->
@@ -1213,9 +1211,6 @@
<!-- Alarm template for far alarms [CHAR LIMIT=25] -->
<string name="alarm_template_far">on <xliff:g id="when" example="Fri 7:00 AM">%1$s</xliff:g></string>
- <!-- Accessibility label for Quick Settings detail screens [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_detail">Quick Settings, <xliff:g id="title" example="Wi-Fi">%s</xliff:g>.</string>
-
<!-- Accessibility label for hotspot icon [CHAR LIMIT=NONE] -->
<string name="accessibility_status_bar_hotspot">Hotspot</string>
@@ -1731,11 +1726,6 @@
<string name="data_connection_no_internet">No internet</string>
<!-- accessibility label for quick settings items that open a details page [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_open_details">Open details.</string>
-
- <!-- accessibility label for quick settings items that are currently disabled. Must have a reason [CHAR LIMIT=NONE] -->
-
- <!-- accessibility label for quick settings items that open a details page [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_open_settings">Open <xliff:g name="page" example="Bluetooth">%s</xliff:g> settings.</string>
<!-- accessibility label for button to edit quick settings [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 9448d3f..5d252fd 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -96,17 +96,12 @@
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
- <style name="TextAppearance.QS.DetailHeader">
- <item name="android:textSize">@dimen/qs_detail_header_text_size</item>
- <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
- </style>
-
<style name="TextAppearance.QS.DetailItemPrimary">
- <item name="android:textSize">@dimen/qs_detail_item_primary_text_size</item>
+ <item name="android:textSize">@dimen/qs_tile_text_size</item>
</style>
<style name="TextAppearance.QS.DetailItemSecondary">
- <item name="android:textSize">@dimen/qs_detail_item_secondary_text_size</item>
+ <item name="android:textSize">@dimen/qs_tile_text_size</item>
<item name="android:textColor">?android:attr/colorAccent</item>
</style>
@@ -120,23 +115,6 @@
<item name="android:textColor">?android:attr/colorError</item>
</style>
- <style name="TextAppearance.QS.DetailButton">
- <item name="android:textSize">@dimen/qs_detail_button_text_size</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
- <item name="android:textAllCaps">true</item>
- <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
- <item name="android:gravity">center</item>
- </style>
-
- <style name="TextAppearance.QS.DetailButton.White">
- <item name="android:textColor">@color/zen_introduction</item>
- </style>
-
- <style name="TextAppearance.QS.DetailEmpty">
- <item name="android:textSize">@dimen/qs_detail_empty_text_size</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
- </style>
-
<style name="TextAppearance.QS.SegmentedButton">
<item name="android:textSize">16sp</item>
<item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
@@ -167,7 +145,7 @@
</style>
<style name="TextAppearance.QS.UserSwitcher">
- <item name="android:textSize">@dimen/qs_detail_item_secondary_text_size</item>
+ <item name="android:textSize">@dimen/qs_tile_text_size</item>
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
@@ -433,9 +411,6 @@
<item name="numColumns">3</item>
</style>
- <style name="AutoSizingList">
- <item name="enableAutoSizing">true</item>
- </style>
<style name="Theme.SystemUI.MediaProjectionAlertDialog">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index 72b40d4..54664f2 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -48,7 +48,7 @@
@Override
public void start() {
if (DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED, false)) {
+ DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED, true)) {
mClipboardManager = requireNonNull(mContext.getSystemService(ClipboardManager.class));
mClipboardManager.addPrimaryClipChangedListener(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 63d4d6b..a9e310d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -105,17 +105,7 @@
updateUdfpsController();
if (mUdfpsController == null) {
- mAuthController.addCallback(new AuthController.Callback() {
- @Override
- public void onAllAuthenticatorsRegistered() {
- updateUdfpsController();
- }
-
- @Override
- public void onEnrollmentsChanged() {
- updateUdfpsController();
- }
- });
+ mAuthController.addCallback(mAuthControllerCallback);
}
}
@@ -128,6 +118,11 @@
}
@Override
+ public void destroy() {
+ mAuthController.removeCallback(mAuthControllerCallback);
+ }
+
+ @Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
int screenState = newState.screenState(mParameters);
mDozeHost.cancelGentleSleep();
@@ -234,4 +229,16 @@
mWakeLock.setAcquired(false);
}
}
+
+ private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
+ @Override
+ public void onAllAuthenticatorsRegistered() {
+ updateUdfpsController();
+ }
+
+ @Override
+ public void onEnrollmentsChanged() {
+ updateUdfpsController();
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index dd91f6f..1ba6e34 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -95,6 +95,10 @@
/***************************************/
// 500 - quick settings
+ /**
+ * @deprecated Not needed anymore
+ */
+ @Deprecated
public static final BooleanFlag NEW_USER_SWITCHER =
new BooleanFlag(500, true);
@@ -144,6 +148,11 @@
public static final BooleanFlag MEDIA_SESSION_LAYOUT = new BooleanFlag(902, false);
public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true);
+ // 1000 - dock
+ public static final BooleanFlag SIMULATE_DOCK_THROUGH_CHARGING =
+ new BooleanFlag(1000, true);
+
+
// Pay no attention to the reflection behind the curtain.
// ========================== Curtain ==========================
// | |
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayoutLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayoutLite.java
index f1e5b08..42230ae 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayoutLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayoutLite.java
@@ -16,8 +16,6 @@
package com.android.systemui.globalactions;
-import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
-
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
@@ -33,15 +31,9 @@
* ConstraintLayout implementation of the button layout created by the global actions dialog.
*/
public class GlobalActionsLayoutLite extends GlobalActionsLayout {
- private final int mMaxColumns;
- private final int mMaxRows;
public GlobalActionsLayoutLite(Context context, AttributeSet attrs) {
super(context, attrs);
- mMaxColumns = getResources().getInteger(
- com.android.systemui.R.integer.power_menu_lite_max_columns);
- mMaxRows = getResources().getInteger(
- com.android.systemui.R.integer.power_menu_lite_max_rows);
setOnClickListener(v -> { }); // Prevent parent onClickListener from triggering
}
@@ -60,10 +52,13 @@
@Override
public void onUpdateList() {
super.onUpdateList();
- int nElementsWrap = (getCurrentRotation() == ROTATION_NONE) ? mMaxColumns : mMaxRows;
+ int nElementsWrap = getResources().getInteger(
+ com.android.systemui.R.integer.power_menu_lite_max_columns);
int nChildren = getListView().getChildCount() - 1; // don't count flow element
- if (getCurrentRotation() != ROTATION_NONE && nChildren > mMaxRows) {
- // up to 4 elements can fit in a row in landscape, otherwise limit for balance
+
+ // Avoid having just one action on the last row if there are more than 2 columns because
+ // it looks unbalanced. Instead, bring the column size down to balance better.
+ if (nChildren == nElementsWrap + 1 && nElementsWrap > 2) {
nElementsWrap -= 1;
}
Flow flow = findViewById(R.id.list_flow);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index b3e6682..ce7a697 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -371,13 +371,6 @@
// Output switcher chip
ViewGroup seamlessView = mMediaViewHolder.getSeamless();
seamlessView.setVisibility(View.VISIBLE);
- seamlessView.setOnClickListener(
- v -> {
- if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- mMediaOutputDialogFactory.create(data.getPackageName(), true,
- mMediaViewHolder.getSeamlessButton());
- }
- });
ImageView iconView = mMediaViewHolder.getSeamlessIcon();
TextView deviceName = mMediaViewHolder.getSeamlessText();
final MediaDeviceData device = data.getDevice();
@@ -387,8 +380,8 @@
final float seamlessAlpha = seamlessDisabled ? DISABLED_ALPHA : 1.0f;
mMediaViewHolder.getSeamlessButton().setAlpha(seamlessAlpha);
seamlessView.setEnabled(!seamlessDisabled);
- String deviceString = null;
- if (device != null && device.getEnabled()) {
+ CharSequence deviceString = mContext.getString(R.string.media_seamless_other_device);
+ if (device != null) {
Drawable icon = device.getIcon();
if (icon instanceof AdaptiveIcon) {
AdaptiveIcon aIcon = (AdaptiveIcon) icon;
@@ -399,13 +392,32 @@
}
deviceString = device.getName();
} else {
- // Reset to default
- Log.w(TAG, "Device is null or not enabled: " + device + ", not binding output chip.");
+ // Set to default icon
iconView.setImageResource(R.drawable.ic_media_home_devices);
- deviceString = mContext.getString(R.string.media_seamless_other_device);
}
deviceName.setText(deviceString);
seamlessView.setContentDescription(deviceString);
+ seamlessView.setOnClickListener(
+ v -> {
+ if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return;
+ }
+ if (device.getIntent() != null) {
+ if (device.getIntent().isActivity()) {
+ mActivityStarter.startActivity(
+ device.getIntent().getIntent(), true);
+ } else {
+ try {
+ device.getIntent().send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Device pending intent was canceled");
+ }
+ }
+ } else {
+ mMediaOutputDialogFactory.create(data.getPackageName(), true,
+ mMediaViewHolder.getSeamlessButton());
+ }
+ });
// Dismiss
mMediaViewHolder.getDismissText().setAlpha(isDismissible ? 1 : DISABLED_ALPHA);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index 4b8dfde..500e82e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -164,8 +164,17 @@
)
/** State of the media device. */
-data class MediaDeviceData(
+data class MediaDeviceData
+@JvmOverloads constructor(
+ /** Whether or not to enable the chip */
val enabled: Boolean,
+
+ /** Device icon to show in the chip */
val icon: Drawable?,
- val name: String?
+
+ /** Device display name */
+ val name: CharSequence?,
+
+ /** Optional intent to override the default output switcher for this control */
+ val intent: PendingIntent? = null
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 240ca36..e1ff110 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -27,8 +27,6 @@
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
-import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.ImageDecoder
import android.graphics.drawable.Icon
@@ -129,7 +127,7 @@
private val useQsMediaPlayer: Boolean,
private val systemClock: SystemClock,
private val tunerService: TunerService,
- private val mediaFlags: MediaFlags,
+ private val mediaFlags: MediaFlags
) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener {
companion object {
@@ -170,20 +168,9 @@
/**
* Check whether this notification is an RCN
- * TODO(b/204910409) implement new API for explicitly declaring this
*/
private fun isRemoteCastNotification(sbn: StatusBarNotification): Boolean {
- val pm = context.packageManager
- try {
- val info = pm.getApplicationInfo(sbn.packageName, PackageManager.MATCH_SYSTEM_ONLY)
- if (info.privateFlags and ApplicationInfo.PRIVATE_FLAG_PRIVILEGED != 0) {
- val extras = sbn.notification.extras
- if (extras.containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME)) {
- return true
- }
- }
- } catch (e: PackageManager.NameNotFoundException) { }
- return false
+ return sbn.notification.extras.containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)
}
@Inject
@@ -597,6 +584,25 @@
artist = HybridGroupManager.resolveText(notif)
}
+ // Device name (used for remote cast notifications)
+ var device: MediaDeviceData? = null
+ if (isRemoteCastNotification(sbn)) {
+ val extras = sbn.notification.extras
+ val deviceName = extras.getCharSequence(Notification.EXTRA_MEDIA_REMOTE_DEVICE, null)
+ val deviceIcon = extras.getInt(Notification.EXTRA_MEDIA_REMOTE_ICON, -1)
+ val deviceIntent = extras.getParcelable(Notification.EXTRA_MEDIA_REMOTE_INTENT)
+ as PendingIntent?
+ Log.d(TAG, "$key is RCN for $deviceName")
+
+ if (deviceName != null && deviceIcon > -1) {
+ // Name and icon must be present, but intent may be null
+ val enabled = deviceIntent != null && deviceIntent.isActivity
+ val deviceDrawable = Icon.createWithResource(sbn.packageName, deviceIcon)
+ .loadDrawable(sbn.getPackageContext(context))
+ device = MediaDeviceData(enabled, deviceDrawable, deviceName, deviceIntent)
+ }
+ }
+
// Control buttons
// If flag is enabled and controller has a PlaybackState, create actions from session info
// Otherwise, use the notification actions
@@ -624,7 +630,7 @@
val active = mediaEntries[key]?.active ?: true
onMediaDataLoaded(key, oldKey, MediaData(sbn.normalizedUserId, true, bgColor, app,
smallIcon, artist, song, artWorkIcon, actionIcons, actionsToShowCollapsed,
- semanticActions, sbn.packageName, token, notif.contentIntent, null,
+ semanticActions, sbn.packageName, token, notif.contentIntent, device,
active, resumeAction = resumeAction, playbackLocation = playbackLocation,
notificationKey = key, hasCheckedForResume = hasCheckedForResume,
isPlaying = isPlaying, isClearable = sbn.isClearable(),
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index f972da5..ffae898 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -81,6 +81,12 @@
var entry = entries[key]
if (entry == null || entry?.token != data.token) {
entry?.stop()
+ if (data.device != null) {
+ // If we were already provided device info (e.g. from RCN), keep that and don't
+ // listen for updates, but process once to push updates to listeners
+ processDevice(key, oldKey, data.device)
+ return
+ }
val controller = data.token?.let {
controllerFactory.create(it)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index a9e9f0f..8731a2c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -153,6 +153,10 @@
@VisibleForTesting
void refresh() {
+ refresh(false);
+ }
+
+ void refresh(boolean deviceSetChanged) {
// Update header icon
final int iconRes = getHeaderIconRes();
final IconCompat iconCompat = getHeaderIcon();
@@ -190,7 +194,8 @@
}
if (!mAdapter.isDragging() && !mAdapter.isAnimating()) {
int currentActivePosition = mAdapter.getCurrentActivePosition();
- if (currentActivePosition >= 0 && currentActivePosition < mAdapter.getItemCount()) {
+ if (!deviceSetChanged && currentActivePosition >= 0
+ && currentActivePosition < mAdapter.getItemCount()) {
mAdapter.notifyItemChanged(currentActivePosition);
} else {
mAdapter.notifyDataSetChanged();
@@ -232,6 +237,11 @@
}
@Override
+ public void onDeviceListChanged() {
+ mMainThreadHandler.post(() -> refresh(true));
+ }
+
+ @Override
public void dismissDialog() {
dismiss();
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 2caecf2..4961711 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -171,7 +171,7 @@
@Override
public void onDeviceListUpdate(List<MediaDevice> devices) {
buildMediaDevices(devices);
- mCallback.onRouteChanged();
+ mCallback.onDeviceListChanged();
}
@Override
@@ -570,11 +570,16 @@
void onMediaStoppedOrPaused();
/**
- * Override to handle the device updating.
+ * Override to handle the device status or attributes updating.
*/
void onRouteChanged();
/**
+ * Override to handle the devices set updating.
+ */
+ void onDeviceListChanged();
+
+ /**
* Override to dismiss dialog.
*/
void dismissDialog();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 98b49b1..aa1117c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -31,6 +31,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
@@ -215,9 +216,11 @@
/** @return {@code true} if taskbar is enabled, false otherwise */
private boolean initializeTaskbarIfNecessary() {
if (mIsTablet) {
+ Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary");
// Remove navigation bar when taskbar is showing
removeNavigationBar(mContext.getDisplayId());
mTaskbarDelegate.init(mContext.getDisplayId());
+ Trace.endSection();
} else {
mTaskbarDelegate.destroy();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java b/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java
deleted file mode 100644
index 18d28bf..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2016 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.qs;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.database.DataSetObserver;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.ListAdapter;
-
-import com.android.systemui.R;
-
-/**
- * Similar to a ListView, but it will show only as many items as fit on screen and
- * bind those instead of scrolling.
- */
-public class AutoSizingList extends LinearLayout {
-
- private static final String TAG = "AutoSizingList";
- private final int mItemSize;
- private final Handler mHandler;
-
- @Nullable
- private ListAdapter mAdapter;
- private int mCount;
- private boolean mEnableAutoSizing;
-
- public AutoSizingList(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
-
- mHandler = new Handler();
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AutoSizingList);
- mItemSize = a.getDimensionPixelSize(R.styleable.AutoSizingList_itemHeight, 0);
- mEnableAutoSizing = a.getBoolean(R.styleable.AutoSizingList_enableAutoSizing, true);
- a.recycle();
- }
-
- public void setAdapter(ListAdapter adapter) {
- if (mAdapter != null) {
- mAdapter.unregisterDataSetObserver(mDataObserver);
- }
- mAdapter = adapter;
- if (adapter != null) {
- adapter.registerDataSetObserver(mDataObserver);
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int requestedHeight = MeasureSpec.getSize(heightMeasureSpec);
- if (requestedHeight != 0) {
- int count = getItemCount(requestedHeight);
- if (mCount != count) {
- postRebindChildren();
- mCount = count;
- }
- }
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
- private int getItemCount(int requestedHeight) {
- int desiredCount = getDesiredCount();
- return mEnableAutoSizing ? Math.min(requestedHeight / mItemSize, desiredCount)
- : desiredCount;
- }
-
- private int getDesiredCount() {
- return mAdapter != null ? mAdapter.getCount() : 0;
- }
-
- private void postRebindChildren() {
- mHandler.post(mBindChildren);
- }
-
- private void rebindChildren() {
- if (mAdapter == null) {
- return;
- }
- for (int i = 0; i < mCount; i++) {
- View v = i < getChildCount() ? getChildAt(i) : null;
- View newView = mAdapter.getView(i, v, this);
- if (newView != v) {
- if (v != null) {
- removeView(v);
- }
- addView(newView, i);
- }
- }
- // Ditch extra views.
- while (getChildCount() > mCount) {
- removeViewAt(getChildCount() - 1);
- }
- }
-
- private final Runnable mBindChildren = new Runnable() {
- @Override
- public void run() {
- rebindChildren();
- }
- };
-
- private final DataSetObserver mDataObserver = new DataSetObserver() {
- @Override
- public void onChanged() {
- if (mCount > getDesiredCount()) {
- mCount = getDesiredCount();
- }
- postRebindChildren();
- }
-
- @Override
- public void onInvalidated() {
- postRebindChildren();
- }
- };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 707313f..f868055 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -44,7 +44,6 @@
private final float[] mFancyClippingRadii = new float[] {0, 0, 0, 0, 0, 0, 0, 0};
private final Path mFancyClippingPath = new Path();
private int mHeightOverride = -1;
- private View mQSDetail;
private QuickStatusBarHeader mHeader;
private float mQsExpansion;
private QSCustomizer mQSCustomizer;
@@ -63,7 +62,6 @@
protected void onFinishInflate() {
super.onFinishInflate();
mQSPanelContainer = findViewById(R.id.expanded_qs_scroll_view);
- mQSDetail = findViewById(R.id.qs_detail);
mHeader = findViewById(R.id.header);
mQSCustomizer = findViewById(R.id.qs_customize);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
@@ -175,9 +173,6 @@
int height = calculateContainerHeight();
int scrollBottom = calculateContainerBottom();
setBottom(getTop() + height);
- mQSDetail.setBottom(getTop() + scrollBottom);
- int qsDetailBottomMargin = ((MarginLayoutParams) mQSDetail.getLayoutParams()).bottomMargin;
- mQSDetail.setBottom(getTop() + scrollBottom - qsDetailBottomMargin);
}
protected int calculateContainerHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
deleted file mode 100644
index 04e2252..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * Copyright (C) 2016 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.qs;
-
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_MORE_SETTINGS;
-
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.AnimatorListenerAdapter;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.drawable.Animatable;
-import android.util.AttributeSet;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.view.WindowInsets;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.Switch;
-import android.widget.TextView;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.policy.SystemBarUtils;
-import com.android.systemui.Dependency;
-import com.android.systemui.FontSizeUtils;
-import com.android.systemui.R;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
-import com.android.systemui.plugins.qs.QSContainerController;
-import com.android.systemui.statusbar.CommandQueue;
-
-public class QSDetail extends LinearLayout {
-
- private static final String TAG = "QSDetail";
- private static final long FADE_DURATION = 300;
-
- private final SparseArray<View> mDetailViews = new SparseArray<>();
- private final UiEventLogger mUiEventLogger = QSEvents.INSTANCE.getQsUiEventsLogger();
-
- private ViewGroup mDetailContent;
- private FalsingManager mFalsingManager;
- protected TextView mDetailSettingsButton;
- protected TextView mDetailDoneButton;
- @VisibleForTesting
- QSDetailClipper mClipper;
- @Nullable
- private DetailAdapter mDetailAdapter;
- private QSPanelController mQsPanelController;
-
- protected View mQsDetailHeader;
- protected TextView mQsDetailHeaderTitle;
- private ViewStub mQsDetailHeaderSwitchStub;
- @Nullable
- private Switch mQsDetailHeaderSwitch;
- protected ImageView mQsDetailHeaderProgress;
-
- @Nullable
- protected QSTileHost mHost;
-
- private boolean mScanState;
- private boolean mClosingDetail;
- private boolean mFullyExpanded;
- private QuickStatusBarHeader mHeader;
- private boolean mTriggeredExpand;
- private boolean mShouldAnimate;
- private int mOpenX;
- private int mOpenY;
- private boolean mAnimatingOpen;
- private boolean mSwitchState;
- private QSFooter mFooter;
-
- @Nullable
- private QSContainerController mQsContainerController;
-
- public QSDetail(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- FontSizeUtils.updateFontSize(mDetailDoneButton, R.dimen.qs_detail_button_text_size);
- FontSizeUtils.updateFontSize(mDetailSettingsButton, R.dimen.qs_detail_button_text_size);
-
- for (int i = 0; i < mDetailViews.size(); i++) {
- mDetailViews.valueAt(i).dispatchConfigurationChanged(newConfig);
- }
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mDetailContent = findViewById(android.R.id.content);
- mDetailSettingsButton = findViewById(android.R.id.button2);
- mDetailDoneButton = findViewById(android.R.id.button1);
-
- mQsDetailHeader = findViewById(R.id.qs_detail_header);
- mQsDetailHeaderTitle = (TextView) mQsDetailHeader.findViewById(android.R.id.title);
- mQsDetailHeaderSwitchStub = mQsDetailHeader.findViewById(R.id.toggle_stub);
- mQsDetailHeaderProgress = findViewById(R.id.qs_detail_header_progress);
-
- updateDetailText();
-
- mClipper = new QSDetailClipper(this);
- }
-
- public void setContainerController(QSContainerController controller) {
- mQsContainerController = controller;
- }
-
- /** */
- public void setQsPanel(QSPanelController panelController, QuickStatusBarHeader header,
- QSFooter footer, FalsingManager falsingManager) {
- mQsPanelController = panelController;
- mHeader = header;
- mFooter = footer;
- mHeader.setCallback(mQsPanelCallback);
- mQsPanelController.setCallback(mQsPanelCallback);
- mFalsingManager = falsingManager;
- }
-
- public void setHost(QSTileHost host) {
- mHost = host;
- }
- public boolean isShowingDetail() {
- return mDetailAdapter != null;
- }
-
- public void setFullyExpanded(boolean fullyExpanded) {
- mFullyExpanded = fullyExpanded;
- }
-
- public void setExpanded(boolean qsExpanded) {
- if (!qsExpanded) {
- mTriggeredExpand = false;
- }
- }
-
- private void updateDetailText() {
- int resId = mDetailAdapter != null ? mDetailAdapter.getDoneText() : Resources.ID_NULL;
- mDetailDoneButton.setText(
- (resId != Resources.ID_NULL) ? resId : R.string.quick_settings_done);
- resId = mDetailAdapter != null ? mDetailAdapter.getSettingsText() : Resources.ID_NULL;
- mDetailSettingsButton.setText(
- (resId != Resources.ID_NULL) ? resId : R.string.quick_settings_more_settings);
- }
-
- public void updateResources() {
- updateDetailText();
- MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
- lp.topMargin = SystemBarUtils.getQuickQsOffsetHeight(mContext);
- setLayoutParams(lp);
- }
-
- @Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- int bottomNavBar = insets.getInsets(WindowInsets.Type.navigationBars()).bottom;
- MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
- lp.bottomMargin = bottomNavBar;
- setLayoutParams(lp);
- return super.onApplyWindowInsets(insets);
- }
-
- public boolean isClosingDetail() {
- return mClosingDetail;
- }
-
- public interface Callback {
- /** Handle an event of showing detail. */
- void onShowingDetail(@Nullable DetailAdapter detail, int x, int y);
- void onToggleStateChanged(boolean state);
- void onScanStateChanged(boolean state);
- }
-
- /** Handle an event of showing detail. */
- public void handleShowingDetail(final @Nullable DetailAdapter adapter, int x, int y,
- boolean toggleQs) {
- final boolean showingDetail = adapter != null;
- final boolean wasShowingDetail = mDetailAdapter != null;
- setClickable(showingDetail);
- if (showingDetail) {
- setupDetailHeader(adapter);
- if (toggleQs && !mFullyExpanded) {
- mTriggeredExpand = true;
- Dependency.get(CommandQueue.class).animateExpandSettingsPanel(null);
- } else {
- mTriggeredExpand = false;
- }
- mShouldAnimate = adapter.shouldAnimate();
- mOpenX = x;
- mOpenY = y;
- } else {
- // Ensure we collapse into the same point we opened from.
- x = mOpenX;
- y = mOpenY;
- if (toggleQs && mTriggeredExpand) {
- Dependency.get(CommandQueue.class).animateCollapsePanels();
- mTriggeredExpand = false;
- }
- // Always animate on close, even if the last opened detail adapter had shouldAnimate()
- // return false. This is necessary to avoid a race condition which could leave the
- // keyguard in a bad state where QS remains visible underneath the notifications, clock,
- // and status area.
- mShouldAnimate = true;
- }
-
- boolean visibleDiff = wasShowingDetail != showingDetail;
- if (!visibleDiff && !wasShowingDetail) return; // already in right state
- AnimatorListener listener;
- if (showingDetail) {
- int viewCacheIndex = adapter.getMetricsCategory();
- View detailView = adapter.createDetailView(mContext, mDetailViews.get(viewCacheIndex),
- mDetailContent);
- if (detailView == null) throw new IllegalStateException("Must return detail view");
-
- setupDetailFooter(adapter);
-
- mDetailContent.removeAllViews();
- mDetailContent.addView(detailView);
- mDetailViews.put(viewCacheIndex, detailView);
- Dependency.get(MetricsLogger.class).visible(adapter.getMetricsCategory());
- mUiEventLogger.log(adapter.openDetailEvent());
- announceForAccessibility(mContext.getString(
- R.string.accessibility_quick_settings_detail,
- adapter.getTitle()));
- mDetailAdapter = adapter;
- listener = mHideGridContentWhenDone;
- setVisibility(View.VISIBLE);
- updateDetailText();
- } else {
- if (wasShowingDetail) {
- Dependency.get(MetricsLogger.class).hidden(mDetailAdapter.getMetricsCategory());
- mUiEventLogger.log(mDetailAdapter.closeDetailEvent());
- }
- mClosingDetail = true;
- mDetailAdapter = null;
- listener = mTeardownDetailWhenDone;
- // Only update visibility if already expanded. Otherwise, a race condition can cause the
- // keyguard to enter a bad state where the QS tiles are displayed underneath the
- // notifications, clock, and status area.
- if (mQsPanelController.isExpanded()) {
- mHeader.setVisibility(View.VISIBLE);
- mFooter.setVisibility(View.VISIBLE);
- mQsPanelController.setGridContentVisibility(true);
- mQsPanelCallback.onScanStateChanged(false);
- }
- }
- sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- animateDetailVisibleDiff(x, y, visibleDiff, listener);
- if (mQsContainerController != null) {
- mQsContainerController.setDetailShowing(showingDetail);
- }
- }
-
- protected void animateDetailVisibleDiff(int x, int y, boolean visibleDiff, AnimatorListener listener) {
- if (visibleDiff) {
- mAnimatingOpen = mDetailAdapter != null;
- if (mFullyExpanded || mDetailAdapter != null) {
- setAlpha(1);
- mClipper.updateCircularClip(mShouldAnimate, x, y, mDetailAdapter != null, listener);
- } else {
- animate().alpha(0)
- .setDuration(mShouldAnimate ? FADE_DURATION : 0)
- .setListener(listener)
- .start();
- }
- }
- }
-
- protected void setupDetailFooter(DetailAdapter adapter) {
- final Intent settingsIntent = adapter.getSettingsIntent();
- mDetailSettingsButton.setVisibility(settingsIntent != null ? VISIBLE : GONE);
- mDetailSettingsButton.setOnClickListener(v -> {
- if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return;
- }
- Dependency.get(MetricsLogger.class).action(ACTION_QS_MORE_SETTINGS,
- adapter.getMetricsCategory());
- mUiEventLogger.log(adapter.moreSettingsEvent());
- Dependency.get(ActivityStarter.class)
- .postStartActivityDismissingKeyguard(settingsIntent, 0);
- });
- mDetailDoneButton.setOnClickListener(v -> {
- if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return;
- }
- announceForAccessibility(
- mContext.getString(R.string.accessibility_desc_quick_settings));
- if (!adapter.onDoneButtonClicked()) {
- mQsPanelController.closeDetail();
- }
- });
- }
-
- protected void setupDetailHeader(final DetailAdapter adapter) {
- mQsDetailHeaderTitle.setText(adapter.getTitle());
- final Boolean toggleState = adapter.getToggleState();
- if (toggleState == null) {
- if (mQsDetailHeaderSwitch != null) mQsDetailHeaderSwitch.setVisibility(INVISIBLE);
- mQsDetailHeader.setClickable(false);
- } else {
- if (mQsDetailHeaderSwitch == null) {
- mQsDetailHeaderSwitch = (Switch) mQsDetailHeaderSwitchStub.inflate();
- }
- mQsDetailHeaderSwitch.setVisibility(VISIBLE);
- handleToggleStateChanged(toggleState, adapter.getToggleEnabled());
- mQsDetailHeader.setClickable(true);
- mQsDetailHeader.setOnClickListener(v -> {
- if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return;
- }
- boolean checked = !mQsDetailHeaderSwitch.isChecked();
- mQsDetailHeaderSwitch.setChecked(checked);
- adapter.setToggleState(checked);
- });
- }
- }
-
- private void handleToggleStateChanged(boolean state, boolean toggleEnabled) {
- mSwitchState = state;
- if (mAnimatingOpen) {
- return;
- }
- if (mQsDetailHeaderSwitch != null) mQsDetailHeaderSwitch.setChecked(state);
- mQsDetailHeader.setEnabled(toggleEnabled);
- if (mQsDetailHeaderSwitch != null) mQsDetailHeaderSwitch.setEnabled(toggleEnabled);
- }
-
- private void handleScanStateChanged(boolean state) {
- if (mScanState == state) return;
- mScanState = state;
- final Animatable anim = (Animatable) mQsDetailHeaderProgress.getDrawable();
- if (state) {
- mQsDetailHeaderProgress.animate().cancel();
- mQsDetailHeaderProgress.animate()
- .alpha(1)
- .withEndAction(anim::start)
- .start();
- } else {
- mQsDetailHeaderProgress.animate().cancel();
- mQsDetailHeaderProgress.animate()
- .alpha(0f)
- .withEndAction(anim::stop)
- .start();
- }
- }
-
- private void checkPendingAnimations() {
- handleToggleStateChanged(mSwitchState,
- mDetailAdapter != null && mDetailAdapter.getToggleEnabled());
- }
-
- protected Callback mQsPanelCallback = new Callback() {
- @Override
- public void onToggleStateChanged(final boolean state) {
- post(new Runnable() {
- @Override
- public void run() {
- handleToggleStateChanged(state,
- mDetailAdapter != null && mDetailAdapter.getToggleEnabled());
- }
- });
- }
-
- @Override
- public void onShowingDetail(
- final @Nullable DetailAdapter detail, final int x, final int y) {
- post(new Runnable() {
- @Override
- public void run() {
- if (isAttachedToWindow()) {
- handleShowingDetail(detail, x, y, false /* toggleQs */);
- }
- }
- });
- }
-
- @Override
- public void onScanStateChanged(final boolean state) {
- post(new Runnable() {
- @Override
- public void run() {
- handleScanStateChanged(state);
- }
- });
- }
- };
-
- private final AnimatorListenerAdapter mHideGridContentWhenDone = new AnimatorListenerAdapter() {
- public void onAnimationCancel(Animator animation) {
- // If we have been cancelled, remove the listener so that onAnimationEnd doesn't get
- // called, this will avoid accidentally turning off the grid when we don't want to.
- animation.removeListener(this);
- mAnimatingOpen = false;
- checkPendingAnimations();
- };
-
- @Override
- public void onAnimationEnd(Animator animation) {
- // Only hide content if still in detail state.
- if (mDetailAdapter != null) {
- mQsPanelController.setGridContentVisibility(false);
- mHeader.setVisibility(View.INVISIBLE);
- mFooter.setVisibility(View.INVISIBLE);
- }
- mAnimatingOpen = false;
- checkPendingAnimations();
- }
- };
-
- private final AnimatorListenerAdapter mTeardownDetailWhenDone = new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animation) {
- mDetailContent.removeAllViews();
- setVisibility(View.INVISIBLE);
- mClosingDetail = false;
- };
- };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
index 43136d3..b02efba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
@@ -25,7 +25,7 @@
import androidx.annotation.Nullable;
-/** Helper for quick settings detail panel clip animations. **/
+/** Helper for quick settings detail panel clip animations. Currently used by the customizer **/
public class QSDetailClipper {
private final View mDetail;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java
deleted file mode 100644
index afd4f0f..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2020 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.qs;
-
-import androidx.annotation.Nullable;
-
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.plugins.qs.DetailAdapter;
-
-import javax.inject.Inject;
-
-/**
- * Proxy class for talking with the QSPanel and showing custom content within it.
- */
-@SysUISingleton
-public class QSDetailDisplayer {
- @Nullable
- private QSPanelController mQsPanelController;
-
- @Inject
- public QSDetailDisplayer() {
- }
-
- public void setQsPanelController(@Nullable QSPanelController qsPanelController) {
- mQsPanelController = qsPanelController;
- }
-
- /** Show the supplied DetailAdapter in the Quick Settings. */
- public void showDetailAdapter(DetailAdapter detailAdapter, int x, int y) {
- if (mQsPanelController != null) {
- mQsPanelController.showDetailAdapter(detailAdapter, x, y);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
deleted file mode 100644
index eb3247b..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright (C) 2014 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.qs;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.Nullable;
-
-import com.android.systemui.FontSizeUtils;
-import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSTile;
-
-/**
- * Quick settings common detail view with line items.
- */
-public class QSDetailItems extends FrameLayout {
- private static final String TAG = "QSDetailItems";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
- private final int mQsDetailIconOverlaySize;
- private final Context mContext;
- private final H mHandler = new H();
- private final Adapter mAdapter = new Adapter();
-
- private String mTag;
- @Nullable
- private Callback mCallback;
- private boolean mItemsVisible = true;
- private AutoSizingList mItemList;
- private View mEmpty;
- private TextView mEmptyText;
- private ImageView mEmptyIcon;
-
- private Item[] mItems;
-
- public QSDetailItems(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
- mTag = TAG;
- mQsDetailIconOverlaySize = (int) getResources().getDimension(
- R.dimen.qs_detail_icon_overlay_size);
- }
-
- public static QSDetailItems convertOrInflate(Context context, View convert, ViewGroup parent) {
- if (convert instanceof QSDetailItems) {
- return (QSDetailItems) convert;
- }
- return (QSDetailItems) LayoutInflater.from(context).inflate(R.layout.qs_detail_items,
- parent, false);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mItemList = findViewById(android.R.id.list);
- mItemList.setVisibility(GONE);
- mItemList.setAdapter(mAdapter);
- mEmpty = findViewById(android.R.id.empty);
- mEmpty.setVisibility(GONE);
- mEmptyText = mEmpty.findViewById(android.R.id.title);
- mEmptyIcon = mEmpty.findViewById(android.R.id.icon);
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- FontSizeUtils.updateFontSize(mEmptyText, R.dimen.qs_detail_empty_text_size);
- int count = mItemList.getChildCount();
- for (int i = 0; i < count; i++) {
- View item = mItemList.getChildAt(i);
- FontSizeUtils.updateFontSize(item, android.R.id.title,
- R.dimen.qs_detail_item_primary_text_size);
- FontSizeUtils.updateFontSize(item, android.R.id.summary,
- R.dimen.qs_detail_item_secondary_text_size);
- }
- }
-
- public void setTagSuffix(String suffix) {
- mTag = TAG + "." + suffix;
- }
-
- public void setEmptyState(int icon, int text) {
- mEmptyIcon.post(() -> {
- mEmptyIcon.setImageResource(icon);
- mEmptyText.setText(text);
- });
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (DEBUG) Log.d(mTag, "onAttachedToWindow");
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
- mCallback = null;
- }
-
- public void setCallback(Callback callback) {
- mHandler.removeMessages(H.SET_CALLBACK);
- mHandler.obtainMessage(H.SET_CALLBACK, callback).sendToTarget();
- }
-
- /** Set items. */
- public void setItems(@Nullable Item[] items) {
- mHandler.removeMessages(H.SET_ITEMS);
- mHandler.obtainMessage(H.SET_ITEMS, items).sendToTarget();
- }
-
- public void setItemsVisible(boolean visible) {
- mHandler.removeMessages(H.SET_ITEMS_VISIBLE);
- mHandler.obtainMessage(H.SET_ITEMS_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
- }
-
- private void handleSetCallback(Callback callback) {
- mCallback = callback;
- }
-
- private void handleSetItems(Item[] items) {
- final int itemCount = items != null ? items.length : 0;
- mEmpty.setVisibility(itemCount == 0 ? VISIBLE : GONE);
- mItemList.setVisibility(itemCount == 0 ? GONE : VISIBLE);
- mItems = items;
- mAdapter.notifyDataSetChanged();
- }
-
- private void handleSetItemsVisible(boolean visible) {
- if (mItemsVisible == visible) return;
- mItemsVisible = visible;
- for (int i = 0; i < mItemList.getChildCount(); i++) {
- mItemList.getChildAt(i).setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
- }
- }
-
- private class Adapter extends BaseAdapter {
-
- @Override
- public int getCount() {
- return mItems != null ? mItems.length : 0;
- }
-
- @Override
- public Object getItem(int position) {
- return mItems[position];
- }
-
- @Override
- public long getItemId(int position) {
- return 0;
- }
-
- @Override
- public View getView(int position, View view, ViewGroup parent) {
- final Item item = mItems[position];
- if (view == null) {
- view = LayoutInflater.from(mContext).inflate(R.layout.qs_detail_item, parent,
- false);
- }
- view.setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
- final ImageView iv = (ImageView) view.findViewById(android.R.id.icon);
- if (item.icon != null) {
- iv.setImageDrawable(item.icon.getDrawable(iv.getContext()));
- } else {
- iv.setImageResource(item.iconResId);
- }
- iv.getOverlay().clear();
- if (item.overlay != null) {
- item.overlay.setBounds(0, 0, mQsDetailIconOverlaySize, mQsDetailIconOverlaySize);
- iv.getOverlay().add(item.overlay);
- }
- final TextView title = (TextView) view.findViewById(android.R.id.title);
- title.setText(item.line1);
- final TextView summary = (TextView) view.findViewById(android.R.id.summary);
- final boolean twoLines = !TextUtils.isEmpty(item.line2);
- title.setMaxLines(twoLines ? 1 : 2);
- summary.setVisibility(twoLines ? VISIBLE : GONE);
- summary.setText(twoLines ? item.line2 : null);
- view.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mCallback != null) {
- mCallback.onDetailItemClick(item);
- }
- }
- });
-
- final ImageView icon2 = (ImageView) view.findViewById(android.R.id.icon2);
- if (item.canDisconnect) {
- icon2.setImageResource(R.drawable.ic_qs_cancel);
- icon2.setVisibility(VISIBLE);
- icon2.setClickable(true);
- icon2.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mCallback != null) {
- mCallback.onDetailItemDisconnect(item);
- }
- }
- });
- } else if (item.icon2 != -1) {
- icon2.setVisibility(VISIBLE);
- icon2.setImageResource(item.icon2);
- icon2.setClickable(false);
- } else {
- icon2.setVisibility(GONE);
- }
-
- return view;
- }
- };
-
- private class H extends Handler {
- private static final int SET_ITEMS = 1;
- private static final int SET_CALLBACK = 2;
- private static final int SET_ITEMS_VISIBLE = 3;
-
- public H() {
- super(Looper.getMainLooper());
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == SET_ITEMS) {
- handleSetItems((Item[]) msg.obj);
- } else if (msg.what == SET_CALLBACK) {
- handleSetCallback((QSDetailItems.Callback) msg.obj);
- } else if (msg.what == SET_ITEMS_VISIBLE) {
- handleSetItemsVisible(msg.arg1 != 0);
- }
- }
- }
-
- public static class Item {
- public Item(int iconResId, CharSequence line1, Object tag) {
- this.iconResId = iconResId;
- this.line1 = line1;
- this.tag = tag;
- }
-
- public int iconResId;
- @Nullable
- public QSTile.Icon icon;
- @Nullable
- public Drawable overlay;
- public CharSequence line1;
- @Nullable
- public CharSequence line2;
- public Object tag;
- public boolean canDisconnect;
- public int icon2 = -1;
- }
-
- public interface Callback {
- void onDetailItemClick(Item item);
- void onDetailItemDisconnect(Item item);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt b/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt
index 26adf46..24bb16a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt
@@ -124,13 +124,13 @@
@UiEvent(doc = "The current user has been switched in the detail panel")
QS_USER_SWITCH(424),
- @UiEvent(doc = "User switcher QS detail panel open")
+ @UiEvent(doc = "User switcher QS dialog open")
QS_USER_DETAIL_OPEN(425),
- @UiEvent(doc = "User switcher QS detail panel closed")
+ @UiEvent(doc = "User switcher QS dialog closed")
QS_USER_DETAIL_CLOSE(426),
- @UiEvent(doc = "User switcher QS detail panel more settings pressed")
+ @UiEvent(doc = "User switcher QS dialog more settings pressed")
QS_USER_MORE_SETTINGS(427),
@UiEvent(doc = "The user has added a guest in the detail panel")
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 50952bd..ea68c40 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -33,7 +33,6 @@
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
-import android.widget.FrameLayout.LayoutParams;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -85,7 +84,6 @@
private QSSquishinessController mQSSquishinessController;
protected QuickStatusBarHeader mHeader;
protected NonInterceptingScrollView mQSPanelScrollView;
- private QSDetail mQSDetail;
private boolean mListening;
private QSContainerImpl mContainer;
private int mLayoutDirection;
@@ -96,8 +94,6 @@
private boolean mQsDisabled;
private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
- private final CommandQueue mCommandQueue;
- private final QSDetailDisplayer mQsDetailDisplayer;
private final MediaHost mQsMediaHost;
private final MediaHost mQqsMediaHost;
private final QSFragmentComponent.Factory mQsComponentFactory;
@@ -126,7 +122,6 @@
* otherwise.
*/
private boolean mInSplitShade;
- private boolean mPulseExpanding;
/**
* Are we currently transitioning from lockscreen to the full shade?
@@ -150,15 +145,13 @@
public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
QSTileHost qsTileHost,
StatusBarStateController statusBarStateController, CommandQueue commandQueue,
- QSDetailDisplayer qsDetailDisplayer, @Named(QS_PANEL) MediaHost qsMediaHost,
+ @Named(QS_PANEL) MediaHost qsMediaHost,
@Named(QUICK_QS_PANEL) MediaHost qqsMediaHost,
KeyguardBypassController keyguardBypassController,
QSFragmentComponent.Factory qsComponentFactory,
QSFragmentDisableFlagsLogger qsFragmentDisableFlagsLogger,
FalsingManager falsingManager, DumpManager dumpManager) {
mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
- mCommandQueue = commandQueue;
- mQsDetailDisplayer = qsDetailDisplayer;
mQsMediaHost = qsMediaHost;
mQqsMediaHost = qqsMediaHost;
mQsComponentFactory = qsComponentFactory;
@@ -209,19 +202,15 @@
mScrollListener.onQsPanelScrollChanged(scrollY);
}
});
- mQSDetail = view.findViewById(R.id.qs_detail);
mHeader = view.findViewById(R.id.header);
mQSPanelController.setHeaderContainer(view.findViewById(R.id.header_text_container));
mFooter = qsFragmentComponent.getQSFooter();
- mQsDetailDisplayer.setQsPanelController(mQSPanelController);
-
mQSContainerImplController = qsFragmentComponent.getQSContainerImplController();
mQSContainerImplController.init();
mContainer = mQSContainerImplController.getView();
mDumpManager.registerDumpable(mContainer.getClass().getName(), mContainer);
- mQSDetail.setQsPanel(mQSPanelController, mHeader, mFooter, mFalsingManager);
mQSAnimator = qsFragmentComponent.getQSAnimator();
mQSSquishinessController = qsFragmentComponent.getQSSquishinessController();
@@ -237,7 +226,6 @@
mQSPanelController.getTileLayout().restoreInstanceState(savedInstanceState);
}
}
- setHost(mHost);
mStatusBarStateController.addCallback(this);
onStateChanged(mStatusBarStateController.getState());
view.addOnLayoutChangeListener(
@@ -271,7 +259,6 @@
setListening(false);
}
mQSCustomizerController.setQs(null);
- mQsDetailDisplayer.setQsPanelController(null);
mScrollListener = null;
mDumpManager.unregisterDumpable(mContainer.getClass().getName());
}
@@ -352,7 +339,6 @@
@Override
public void setContainerController(QSContainerController controller) {
mQSCustomizerController.setContainerController(controller);
- mQSDetail.setContainerController(controller);
}
@Override
@@ -360,10 +346,6 @@
return mQSCustomizerController.isCustomizing();
}
- public void setHost(QSTileHost qsh) {
- mQSDetail.setHost(qsh);
- }
-
@Override
public void disable(int displayId, int state1, int state2, boolean animate) {
if (displayId != getContext().getDisplayId()) {
@@ -392,7 +374,6 @@
final boolean expandVisually = expanded || mStackScrollerOverscrolling
|| mHeaderAnimating;
mQSPanelController.setExpanded(expanded);
- mQSDetail.setExpanded(expanded);
boolean keyguardShowing = isKeyguardState();
mHeader.setVisibility((expanded || !keyguardShowing || mHeaderAnimating
|| mShowCollapsedOnKeyguard)
@@ -444,7 +425,7 @@
@Override
public boolean isShowingDetail() {
- return mQSCustomizerController.isCustomizing() || mQSDetail.isShowingDetail();
+ return mQSCustomizerController.isCustomizing();
}
@Override
@@ -573,7 +554,6 @@
if (fullyCollapsed) {
mQSPanelScrollView.setScrollY(0);
}
- mQSDetail.setFullyExpanded(fullyExpanded);
if (!fullyExpanded) {
// Set bounds on the QS panel so it doesn't run over the header when animating.
@@ -732,14 +712,7 @@
if (mQSCustomizerController.isCustomizing()) {
return getView().getHeight();
}
- if (mQSDetail.isClosingDetail()) {
- LayoutParams layoutParams = (LayoutParams) mQSPanelScrollView.getLayoutParams();
- int panelHeight = layoutParams.topMargin + layoutParams.bottomMargin +
- + mQSPanelScrollView.getMeasuredHeight();
- return panelHeight + getView().getPaddingBottom();
- } else {
- return getView().getMeasuredHeight();
- }
+ return getView().getMeasuredHeight();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 7c04cd4..0c854df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -41,10 +41,8 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.widget.RemeasuringLinearLayout;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.settings.brightness.BrightnessSliderController;
-import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -80,7 +78,6 @@
protected boolean mExpanded;
protected boolean mListening;
- private QSDetail.Callback mCallback;
protected QSTileHost mHost;
private final List<OnConfigurationChangedListener> mOnConfigurationChangedListeners =
new ArrayList<>();
@@ -100,9 +97,6 @@
private int mContentMarginEnd;
private boolean mUsingHorizontalLayout;
- private Record mDetailRecord;
-
- private BrightnessMirrorController mBrightnessMirrorController;
private LinearLayout mHorizontalLinearLayout;
protected LinearLayout mHorizontalContentContainer;
@@ -315,24 +309,12 @@
view.setVisibility(TunerService.parseIntegerSwitch(newValue, true) ? VISIBLE : GONE);
}
- /** */
- public void openDetails(QSTile tile) {
- // If there's no tile with that name (as defined in QSFactoryImpl or other QSFactory),
- // QSFactory will not be able to create a tile and getTile will return null
- if (tile != null) {
- showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0});
- }
- }
@Nullable
View getBrightnessView() {
return mBrightnessView;
}
- public void setCallback(QSDetail.Callback callback) {
- mCallback = callback;
- }
-
/**
* Links the footer's page indicator, which is used in landscape orientation to save space.
*
@@ -519,25 +501,6 @@
mListening = listening;
}
- public void showDetailAdapter(boolean show, DetailAdapter adapter, int[] locationInWindow) {
- int xInWindow = locationInWindow[0];
- int yInWindow = locationInWindow[1];
- ((View) getParent()).getLocationInWindow(locationInWindow);
-
- Record r = new Record();
- r.detailAdapter = adapter;
- r.x = xInWindow - locationInWindow[0];
- r.y = yInWindow - locationInWindow[1];
-
- locationInWindow[0] = xInWindow;
- locationInWindow[1] = yInWindow;
-
- showDetail(show, r);
- }
-
- protected void showDetail(boolean show, Record r) {
- mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0, r).sendToTarget();
- }
protected void drawTile(QSPanelControllerBase.TileRecord r, QSTile.State state) {
r.tileView.onStateChanged(state);
@@ -565,30 +528,6 @@
public void onStateChanged(QSTile.State state) {
drawTile(tileRecord, state);
}
-
- @Override
- public void onShowDetail(boolean show) {
- // Both the collapsed and full QS panels get this callback, this check determines
- // which one should handle showing the detail.
- if (shouldShowDetail()) {
- QSPanel.this.showDetail(show, tileRecord);
- }
- }
-
- @Override
- public void onToggleStateChanged(boolean state) {
- if (mDetailRecord == tileRecord) {
- fireToggleStateChanged(state);
- }
- }
-
- @Override
- public void onScanStateChanged(boolean state) {
- tileRecord.scanState = state;
- if (mDetailRecord == tileRecord) {
- fireScanStateChanged(tileRecord.scanState);
- }
- }
};
tileRecord.tile.addCallback(callback);
@@ -605,72 +544,10 @@
mTileLayout.removeTile(tileRecord);
}
- void closeDetail() {
- showDetail(false, mDetailRecord);
- }
-
public int getGridHeight() {
return getMeasuredHeight();
}
- protected void handleShowDetail(Record r, boolean show) {
- if (r instanceof QSPanelControllerBase.TileRecord) {
- handleShowDetailTile((QSPanelControllerBase.TileRecord) r, show);
- } else {
- int x = 0;
- int y = 0;
- if (r != null) {
- x = r.x;
- y = r.y;
- }
- handleShowDetailImpl(r, show, x, y);
- }
- }
-
- private void handleShowDetailTile(QSPanelControllerBase.TileRecord r, boolean show) {
- if ((mDetailRecord != null) == show && mDetailRecord == r) return;
-
- if (show) {
- r.detailAdapter = r.tile.getDetailAdapter();
- if (r.detailAdapter == null) return;
- }
- r.tile.setDetailListening(show);
- int x = r.tileView.getLeft() + r.tileView.getWidth() / 2;
- int y = r.tileView.getDetailY() + mTileLayout.getOffsetTop(r) + getTop();
- handleShowDetailImpl(r, show, x, y);
- }
-
- private void handleShowDetailImpl(Record r, boolean show, int x, int y) {
- setDetailRecord(show ? r : null);
- fireShowingDetail(show ? r.detailAdapter : null, x, y);
- }
-
- protected void setDetailRecord(Record r) {
- if (r == mDetailRecord) return;
- mDetailRecord = r;
- final boolean scanState = mDetailRecord instanceof QSPanelControllerBase.TileRecord
- && ((QSPanelControllerBase.TileRecord) mDetailRecord).scanState;
- fireScanStateChanged(scanState);
- }
-
- private void fireShowingDetail(DetailAdapter detail, int x, int y) {
- if (mCallback != null) {
- mCallback.onShowingDetail(detail, x, y);
- }
- }
-
- private void fireToggleStateChanged(boolean state) {
- if (mCallback != null) {
- mCallback.onToggleStateChanged(state);
- }
- }
-
- private void fireScanStateChanged(boolean state) {
- if (mCallback != null) {
- mCallback.onScanStateChanged(state);
- }
- }
-
QSTileLayout getTileLayout() {
return mTileLayout;
}
@@ -774,26 +651,16 @@
}
private class H extends Handler {
- private static final int SHOW_DETAIL = 1;
- private static final int SET_TILE_VISIBILITY = 2;
- private static final int ANNOUNCE_FOR_ACCESSIBILITY = 3;
+ private static final int ANNOUNCE_FOR_ACCESSIBILITY = 1;
@Override
public void handleMessage(Message msg) {
- if (msg.what == SHOW_DETAIL) {
- handleShowDetail((Record) msg.obj, msg.arg1 != 0);
- } else if (msg.what == ANNOUNCE_FOR_ACCESSIBILITY) {
+ if (msg.what == ANNOUNCE_FOR_ACCESSIBILITY) {
announceForAccessibility((CharSequence) msg.obj);
}
}
}
- protected static class Record {
- DetailAdapter detailAdapter;
- int x;
- int y;
- }
-
public interface QSTileLayout {
/** */
default void saveInstanceState(Bundle outState) {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 418c4ae..6a7f3c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -36,15 +36,12 @@
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
-import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.brightness.BrightnessController;
import com.android.systemui.settings.brightness.BrightnessMirrorHandler;
import com.android.systemui.settings.brightness.BrightnessSliderController;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.Utils;
@@ -57,7 +54,6 @@
*/
@QSScope
public class QSPanelController extends QSPanelControllerBase<QSPanel> {
- public static final String QS_REMOVE_LABELS = "sysui_remove_labels";
private final QSFgsManagerFooter mQSFgsManagerFooter;
private final QSSecurityFooter mQsSecurityFooter;
@@ -65,7 +61,6 @@
private final QSCustomizerController mQsCustomizerController;
private final QSTileRevealController.Factory mQsTileRevealControllerFactory;
private final FalsingManager mFalsingManager;
- private final CommandQueue mCommandQueue;
private final BrightnessController mBrightnessController;
private final BrightnessSliderController mBrightnessSliderController;
private final BrightnessMirrorHandler mBrightnessMirrorHandler;
@@ -107,7 +102,7 @@
DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
BrightnessSliderController.Factory brightnessSliderFactory,
- FalsingManager falsingManager, CommandQueue commandQueue, FeatureFlags featureFlags) {
+ FalsingManager falsingManager, FeatureFlags featureFlags) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
metricsLogger, uiEventLogger, qsLogger, dumpManager);
mQSFgsManagerFooter = qsFgsManagerFooter;
@@ -116,7 +111,6 @@
mQsCustomizerController = qsCustomizerController;
mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
mFalsingManager = falsingManager;
- mCommandQueue = commandQueue;
mBrightnessSliderController = brightnessSliderFactory.create(getContext(), mView);
mView.setBrightnessView(mBrightnessSliderController.getRootView());
@@ -221,15 +215,6 @@
return mHost;
}
-
- /** Open the details for a specific tile.. */
- public void openDetails(String subPanel) {
- QSTile tile = getTile(subPanel);
- if (tile != null) {
- mView.openDetails(tile);
- }
- }
-
/** Show the device monitoring dialog. */
public void showDeviceMonitoringDialog() {
mQsSecurityFooter.showDeviceMonitoringDialog();
@@ -261,11 +246,6 @@
}
/** */
- public void setCallback(QSDetail.Callback qsPanelCallback) {
- mView.setCallback(qsPanelCallback);
- }
-
- /** */
public void setGridContentVisibility(boolean visible) {
int newVis = visible ? View.VISIBLE : View.INVISIBLE;
setVisibility(newVis);
@@ -294,19 +274,6 @@
}
/** */
- public void showDetailAdapter(DetailAdapter detailAdapter, int x, int y) {
- // TODO(b/199296365)
- // Workaround for opening detail from QQS, when there might not be enough space to
- // display e.g. in case of multiuser detail from split shade. Currently showing detail works
- // only for QS (mView below) and that's why expanding panel (thus showing QS instead of QQS)
- // makes it displayed correctly.
- if (!isExpanded()) {
- mCommandQueue.animateExpandSettingsPanel(null);
- }
- mView.showDetailAdapter(true, detailAdapter, new int[]{x, y});
- }
-
- /** */
public void setFooterPageIndicator(PageIndicator pageIndicator) {
mView.setFooterPageIndicator(pageIndicator);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index d4d6da8..0bff722 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -298,21 +298,8 @@
mQsCustomizerController.hide();
return;
}
- mView.closeDetail();
}
- /** */
- public void openDetails(String subPanel) {
- QSTile tile = getTile(subPanel);
- // If there's no tile with that name (as defined in QSFactoryImpl or other QSFactory),
- // QSFactory will not be able to create a tile and getTile will return null
- if (tile != null) {
- mView.showDetailAdapter(
- true, tile.getDetailAdapter(), new int[]{mView.getWidth() / 2, 0});
- }
- }
-
-
void setListening(boolean listening) {
mView.setListening(listening);
@@ -429,7 +416,7 @@
}
/** */
- public static final class TileRecord extends QSPanel.Record {
+ public static final class TileRecord {
public TileRecord(QSTile tile, com.android.systemui.plugins.qs.QSTileView tileView) {
this.tile = tile;
this.tileView = tileView;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index d3f8db38..8c08873 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -39,7 +39,6 @@
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
-import com.android.systemui.qs.QSDetail.Callback;
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
import com.android.systemui.statusbar.phone.StatusIconContainer;
@@ -548,10 +547,6 @@
post(() -> setClickable(!mExpanded));
}
- public void setCallback(Callback qsPanelCallback) {
- mHeaderQsPanel.setCallback(qsPanelCallback);
- }
-
private void setContentMargins(View view, int marginStart, int marginEnd) {
MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams();
lp.setMarginStart(marginStart);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index b29687f..32a7916 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -205,15 +205,6 @@
finished();
}
}
-
- @Override
- public void onShowDetail(boolean show) {}
-
- @Override
- public void onToggleStateChanged(boolean state) {}
-
- @Override
- public void onScanStateChanged(boolean state) {}
}
private void addPackageTiles(final QSTileHost host) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 86fc4de..1488231 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -53,7 +53,6 @@
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.qs.tiles.ScreenRecordTile;
import com.android.systemui.qs.tiles.UiModeNightTile;
-import com.android.systemui.qs.tiles.UserTile;
import com.android.systemui.qs.tiles.WifiTile;
import com.android.systemui.qs.tiles.WorkModeTile;
import com.android.systemui.util.leak.GarbageMonitor;
@@ -82,7 +81,6 @@
private final Provider<LocationTile> mLocationTileProvider;
private final Provider<CastTile> mCastTileProvider;
private final Provider<HotspotTile> mHotspotTileProvider;
- private final Provider<UserTile> mUserTileProvider;
private final Provider<BatterySaverTile> mBatterySaverTileProvider;
private final Provider<DataSaverTile> mDataSaverTileProvider;
private final Provider<NightDisplayTile> mNightDisplayTileProvider;
@@ -119,7 +117,6 @@
Provider<LocationTile> locationTileProvider,
Provider<CastTile> castTileProvider,
Provider<HotspotTile> hotspotTileProvider,
- Provider<UserTile> userTileProvider,
Provider<BatterySaverTile> batterySaverTileProvider,
Provider<DataSaverTile> dataSaverTileProvider,
Provider<NightDisplayTile> nightDisplayTileProvider,
@@ -152,7 +149,6 @@
mLocationTileProvider = locationTileProvider;
mCastTileProvider = castTileProvider;
mHotspotTileProvider = hotspotTileProvider;
- mUserTileProvider = userTileProvider;
mBatterySaverTileProvider = batterySaverTileProvider;
mDataSaverTileProvider = dataSaverTileProvider;
mNightDisplayTileProvider = nightDisplayTileProvider;
@@ -212,8 +208,6 @@
return mCastTileProvider.get();
case "hotspot":
return mHotspotTileProvider.get();
- case "user":
- return mUserTileProvider.get();
case "battery":
return mBatterySaverTileProvider.get();
case "saver":
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 6d9d5b1..131589f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -60,7 +60,6 @@
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.State;
@@ -261,16 +260,6 @@
return new QSIconViewImpl(context);
}
- /** Returns corresponding DetailAdapter. */
- @Nullable
- public DetailAdapter getDetailAdapter() {
- return null; // optional
- }
-
- protected DetailAdapter createDetailAdapter() {
- throw new UnsupportedOperationException();
- }
-
/**
* Is a startup check whether this device currently supports this tile.
* Should not be used to conditionally hide tiles. Only checked on tile
@@ -337,10 +326,6 @@
.addTaggedData(FIELD_QS_POSITION, mHost.indexOf(mTileSpec));
}
- public void showDetail(boolean show) {
- mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0).sendToTarget();
- }
-
public void refreshState() {
refreshState(null);
}
@@ -353,14 +338,6 @@
mHandler.obtainMessage(H.USER_SWITCH, newUserId, 0).sendToTarget();
}
- public void fireToggleStateChanged(boolean state) {
- mHandler.obtainMessage(H.TOGGLE_STATE_CHANGED, state ? 1 : 0, 0).sendToTarget();
- }
-
- public void fireScanStateChanged(boolean state) {
- mHandler.obtainMessage(H.SCAN_STATE_CHANGED, state ? 1 : 0, 0).sendToTarget();
- }
-
public void destroy() {
mHandler.sendEmptyMessage(H.DESTROY);
}
@@ -462,29 +439,6 @@
}
}
- private void handleShowDetail(boolean show) {
- mShowingDetail = show;
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onShowDetail(show);
- }
- }
-
- protected boolean isShowingDetail() {
- return mShowingDetail;
- }
-
- private void handleToggleStateChanged(boolean state) {
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onToggleStateChanged(state);
- }
- }
-
- private void handleScanStateChanged(boolean state) {
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onScanStateChanged(state);
- }
- }
-
protected void handleUserSwitch(int newUserId) {
handleRefreshState(null);
}
@@ -591,17 +545,14 @@
private static final int SECONDARY_CLICK = 3;
private static final int LONG_CLICK = 4;
private static final int REFRESH_STATE = 5;
- private static final int SHOW_DETAIL = 6;
- private static final int USER_SWITCH = 7;
- private static final int TOGGLE_STATE_CHANGED = 8;
- private static final int SCAN_STATE_CHANGED = 9;
- private static final int DESTROY = 10;
- private static final int REMOVE_CALLBACKS = 11;
- private static final int REMOVE_CALLBACK = 12;
- private static final int SET_LISTENING = 13;
+ private static final int USER_SWITCH = 6;
+ private static final int DESTROY = 7;
+ private static final int REMOVE_CALLBACKS = 8;
+ private static final int REMOVE_CALLBACK = 9;
+ private static final int SET_LISTENING = 10;
@VisibleForTesting
- protected static final int STALE = 14;
- private static final int INITIALIZE = 15;
+ protected static final int STALE = 11;
+ private static final int INITIALIZE = 12;
@VisibleForTesting
protected H(Looper looper) {
@@ -639,18 +590,9 @@
} else if (msg.what == REFRESH_STATE) {
name = "handleRefreshState";
handleRefreshState(msg.obj);
- } else if (msg.what == SHOW_DETAIL) {
- name = "handleShowDetail";
- handleShowDetail(msg.arg1 != 0);
} else if (msg.what == USER_SWITCH) {
name = "handleUserSwitch";
handleUserSwitch(msg.arg1);
- } else if (msg.what == TOGGLE_STATE_CHANGED) {
- name = "handleToggleStateChanged";
- handleToggleStateChanged(msg.arg1 != 0);
- } else if (msg.what == SCAN_STATE_CHANGED) {
- name = "handleScanStateChanged";
- handleScanStateChanged(msg.arg1 != 0);
} else if (msg.what == DESTROY) {
name = "handleDestroy";
handleDestroy();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 754f8e2..c61c18a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -19,7 +19,6 @@
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
@@ -29,7 +28,6 @@
import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.Switch;
import androidx.annotation.Nullable;
@@ -38,24 +36,18 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSDetailItems;
-import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.BluetoothController;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
import javax.inject.Inject;
@@ -65,7 +57,6 @@
private static final Intent BLUETOOTH_SETTINGS = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
private final BluetoothController mController;
- private final BluetoothDetailAdapter mDetailAdapter;
@Inject
public BluetoothTile(
@@ -82,16 +73,10 @@
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mController = bluetoothController;
- mDetailAdapter = (BluetoothDetailAdapter) createDetailAdapter();
mController.observe(getLifecycle(), mCallback);
}
@Override
- public DetailAdapter getDetailAdapter() {
- return mDetailAdapter;
- }
-
- @Override
public BooleanState newTileState() {
return new BooleanState();
}
@@ -117,7 +102,6 @@
new Intent(Settings.ACTION_BLUETOOTH_SETTINGS), 0);
return;
}
- showDetail(true);
if (!mState.value) {
mController.setBluetoothEnabled(true);
}
@@ -251,53 +235,14 @@
@Override
public void onBluetoothStateChange(boolean enabled) {
refreshState();
- if (isShowingDetail()) {
- mDetailAdapter.updateItems();
- fireToggleStateChanged(mDetailAdapter.getToggleState());
- }
}
@Override
public void onBluetoothDevicesChanged() {
refreshState();
- if (isShowingDetail()) {
- mDetailAdapter.updateItems();
- }
}
};
- @Override
- protected DetailAdapter createDetailAdapter() {
- return new BluetoothDetailAdapter();
- }
-
- /**
- * Bluetooth icon wrapper for Quick Settings with a battery indicator that reflects the
- * connected device's battery level. This is used instead of
- * {@link com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon} in order to use a context
- * that reflects dark/light theme attributes.
- */
- private class BluetoothBatteryTileIcon extends Icon {
- private int mBatteryLevel;
- private float mIconScale;
-
- BluetoothBatteryTileIcon(int batteryLevel, float iconScale) {
- mBatteryLevel = batteryLevel;
- mIconScale = iconScale;
- }
-
- @Override
- public Drawable getDrawable(Context context) {
- // This method returns Pair<Drawable, String> while first value is the drawable
- return BluetoothDeviceLayerDrawable.createLayerDrawable(
- context,
- R.drawable.ic_bluetooth_connected,
- mBatteryLevel,
- mIconScale);
- }
- }
-
-
/**
* Bluetooth icon wrapper (when connected with no battery indicator) for Quick Settings. This is
* used instead of {@link com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon} in order to
@@ -315,129 +260,4 @@
return context.getDrawable(R.drawable.ic_bluetooth_connected);
}
}
-
- protected class BluetoothDetailAdapter implements DetailAdapter, QSDetailItems.Callback {
- // We probably won't ever have space in the UI for more than 20 devices, so don't
- // get info for them.
- private static final int MAX_DEVICES = 20;
- @Nullable
- private QSDetailItems mItems;
-
- @Override
- public CharSequence getTitle() {
- return mContext.getString(R.string.quick_settings_bluetooth_label);
- }
-
- @Override
- public Boolean getToggleState() {
- return mState.value;
- }
-
- @Override
- public boolean getToggleEnabled() {
- return mController.getBluetoothState() == BluetoothAdapter.STATE_OFF
- || mController.getBluetoothState() == BluetoothAdapter.STATE_ON;
- }
-
- @Override
- public Intent getSettingsIntent() {
- return BLUETOOTH_SETTINGS;
- }
-
- @Override
- public void setToggleState(boolean state) {
- MetricsLogger.action(mContext, MetricsEvent.QS_BLUETOOTH_TOGGLE, state);
- mController.setBluetoothEnabled(state);
- }
-
- @Override
- public int getMetricsCategory() {
- return MetricsEvent.QS_BLUETOOTH_DETAILS;
- }
-
- @Override
- public View createDetailView(Context context, View convertView, ViewGroup parent) {
- mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
- mItems.setTagSuffix("Bluetooth");
- mItems.setCallback(this);
- updateItems();
- setItemsVisible(mState.value);
- return mItems;
- }
-
- public void setItemsVisible(boolean visible) {
- if (mItems == null) return;
- mItems.setItemsVisible(visible);
- }
-
- private void updateItems() {
- if (mItems == null) return;
- if (mController.isBluetoothEnabled()) {
- mItems.setEmptyState(R.drawable.ic_qs_bluetooth_detail_empty,
- R.string.quick_settings_bluetooth_detail_empty_text);
- } else {
- mItems.setEmptyState(R.drawable.ic_qs_bluetooth_detail_empty,
- R.string.bt_is_off);
- }
- ArrayList<Item> items = new ArrayList<Item>();
- final Collection<CachedBluetoothDevice> devices = mController.getDevices();
- if (devices != null) {
- int connectedDevices = 0;
- int count = 0;
- for (CachedBluetoothDevice device : devices) {
- if (mController.getBondState(device) == BluetoothDevice.BOND_NONE) continue;
- final Item item =
- new Item(
- com.android.internal.R.drawable.ic_qs_bluetooth,
- device.getName(),
- device);
- int state = device.getMaxConnectionState();
- if (state == BluetoothProfile.STATE_CONNECTED) {
- item.iconResId = R.drawable.ic_bluetooth_connected;
- int batteryLevel = device.getBatteryLevel();
- if (batteryLevel > BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
- item.icon = new BluetoothBatteryTileIcon(batteryLevel,1 /* iconScale */);
- item.line2 = mContext.getString(
- R.string.quick_settings_connected_battery_level,
- Utils.formatPercentage(batteryLevel));
- } else {
- item.line2 = mContext.getString(R.string.quick_settings_connected);
- }
- item.canDisconnect = true;
- items.add(connectedDevices, item);
- connectedDevices++;
- } else if (state == BluetoothProfile.STATE_CONNECTING) {
- item.iconResId = R.drawable.ic_qs_bluetooth_connecting;
- item.line2 = mContext.getString(R.string.quick_settings_connecting);
- items.add(connectedDevices, item);
- } else {
- items.add(item);
- }
- if (++count == MAX_DEVICES) {
- break;
- }
- }
- }
- mItems.setItems(items.toArray(new Item[items.size()]));
- }
-
- @Override
- public void onDetailItemClick(Item item) {
- if (item == null || item.tag == null) return;
- final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
- if (device != null && device.getMaxConnectionState()
- == BluetoothProfile.STATE_DISCONNECTED) {
- mController.connect(device);
- }
- }
-
- @Override
- public void onDetailItemDisconnect(Item item) {
- if (item == null || item.tag == null) return;
- final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
- if (device != null) {
- mController.disconnect(device);
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 76c84f9..4d9c4c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -20,7 +20,6 @@
import android.annotation.NonNull;
import android.app.Dialog;
-import android.content.Context;
import android.content.Intent;
import android.media.MediaRouter.RouteInfo;
import android.os.Handler;
@@ -29,8 +28,6 @@
import android.service.quicksettings.Tile;
import android.util.Log;
import android.view.View;
-import android.view.View.OnAttachStateChangeListener;
-import android.view.ViewGroup;
import android.widget.Button;
import androidx.annotation.Nullable;
@@ -44,11 +41,8 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSDetailItems;
-import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -62,7 +56,6 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.util.ArrayList;
-import java.util.LinkedHashMap;
import java.util.List;
import javax.inject.Inject;
@@ -73,7 +66,6 @@
new Intent(Settings.ACTION_CAST_SETTINGS);
private final CastController mController;
- private final CastDetailAdapter mDetailAdapter;
private final KeyguardStateController mKeyguard;
private final NetworkController mNetworkController;
private final DialogLaunchAnimator mDialogLaunchAnimator;
@@ -100,7 +92,6 @@
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mController = castController;
- mDetailAdapter = new CastDetailAdapter();
mKeyguard = keyguardStateController;
mNetworkController = networkController;
mDialogLaunchAnimator = dialogLaunchAnimator;
@@ -111,11 +102,6 @@
}
@Override
- public DetailAdapter getDetailAdapter() {
- return mDetailAdapter;
- }
-
- @Override
public BooleanState newTileState() {
BooleanState state = new BooleanState();
state.handlesLongClick = false;
@@ -154,14 +140,14 @@
}
List<CastDevice> activeDevices = getActiveDevices();
- if (willPopDetail()) {
+ if (willPopDialog()) {
if (!mKeyguard.isShowing()) {
- showDetail(view);
+ showDialog(view);
} else {
mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
// Dismissing the keyguard will collapse the shade, so we don't animate from the
// view here as it would not look good.
- showDetail(null /* view */);
+ showDialog(null /* view */);
});
}
} else {
@@ -173,7 +159,7 @@
// (neither routes nor projection), or if we have an active route. In other cases, we assume
// that a projection is active. This is messy, but this tile never correctly handled the
// case where multiple devices were active :-/.
- private boolean willPopDetail() {
+ private boolean willPopDialog() {
List<CastDevice> activeDevices = getActiveDevices();
return activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo);
}
@@ -190,11 +176,6 @@
return activeDevices;
}
- @Override
- public void showDetail(boolean show) {
- showDetail(null /* view */);
- }
-
private static class DialogHolder {
private Dialog mDialog;
@@ -203,7 +184,7 @@
}
}
- private void showDetail(@Nullable View view) {
+ private void showDialog(@Nullable View view) {
mUiHandler.post(() -> {
final DialogHolder holder = new DialogHolder();
final Dialog dialog = MediaRouteDialogPresenter.createDialog(
@@ -268,10 +249,8 @@
if (!state.value) {
state.secondaryLabel = "";
}
- state.contentDescription = state.contentDescription + ","
- + mContext.getString(R.string.accessibility_quick_settings_open_details);
state.expandedAccessibilityClassName = Button.class.getName();
- state.forceExpandIcon = willPopDetail();
+ state.forceExpandIcon = willPopDialog();
} else {
state.state = Tile.STATE_UNAVAILABLE;
String noWifi = mContext.getString(R.string.quick_settings_cast_no_wifi);
@@ -279,7 +258,6 @@
state.forceExpandIcon = false;
}
state.stateDescription = state.stateDescription + ", " + state.secondaryLabel;
- mDetailAdapter.updateItems(devices);
}
@Override
@@ -339,118 +317,4 @@
refreshState();
}
};
-
- private final class CastDetailAdapter implements DetailAdapter, QSDetailItems.Callback {
- private final LinkedHashMap<String, CastDevice> mVisibleOrder = new LinkedHashMap<>();
-
- private QSDetailItems mItems;
-
- @Override
- public CharSequence getTitle() {
- return mContext.getString(R.string.quick_settings_cast_title);
- }
-
- @Override
- public Boolean getToggleState() {
- return null;
- }
-
- @Override
- public Intent getSettingsIntent() {
- return CAST_SETTINGS;
- }
-
- @Override
- public void setToggleState(boolean state) {
- // noop
- }
-
- @Override
- public int getMetricsCategory() {
- return MetricsEvent.QS_CAST_DETAILS;
- }
-
- @Override
- public View createDetailView(Context context, View convertView, ViewGroup parent) {
- mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
- mItems.setTagSuffix("Cast");
- if (convertView == null) {
- if (DEBUG) Log.d(TAG, "addOnAttachStateChangeListener");
- mItems.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- if (DEBUG) Log.d(TAG, "onViewAttachedToWindow");
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- if (DEBUG) Log.d(TAG, "onViewDetachedFromWindow");
- mVisibleOrder.clear();
- }
- });
- }
- mItems.setEmptyState(R.drawable.ic_qs_cast_detail_empty,
- R.string.quick_settings_cast_detail_empty_text);
- mItems.setCallback(this);
- updateItems(mController.getCastDevices());
- mController.setDiscovering(true);
- return mItems;
- }
-
- private void updateItems(List<CastDevice> devices) {
- if (mItems == null) return;
- Item[] items = null;
- if (devices != null && !devices.isEmpty()) {
- // if we are connected, simply show that device
- for (CastDevice device : devices) {
- if (device.state == CastDevice.STATE_CONNECTED) {
- final Item item =
- new Item(
- R.drawable.ic_cast_connected,
- getDeviceName(device),
- device);
- item.line2 = mContext.getString(R.string.quick_settings_connected);
- item.canDisconnect = true;
- items = new Item[] { item };
- break;
- }
- }
- // otherwise list all available devices, and don't move them around
- if (items == null) {
- for (CastDevice device : devices) {
- mVisibleOrder.put(device.id, device);
- }
- items = new Item[devices.size()];
- int i = 0;
- for (String id : mVisibleOrder.keySet()) {
- final CastDevice device = mVisibleOrder.get(id);
- if (!devices.contains(device)) continue;
- final Item item =
- new Item(R.drawable.ic_cast, getDeviceName(device), device);
- if (device.state == CastDevice.STATE_CONNECTING) {
- item.line2 = mContext.getString(R.string.quick_settings_connecting);
- }
- items[i++] = item;
- }
- }
- }
- mItems.setItems(items);
- }
-
- @Override
- public void onDetailItemClick(Item item) {
- if (item == null || item.tag == null) return;
- MetricsLogger.action(mContext, MetricsEvent.QS_CAST_SELECT);
- final CastDevice device = (CastDevice) item.tag;
- mController.startCasting(device);
- }
-
- @Override
- public void onDetailItemDisconnect(Item item) {
- if (item == null || item.tag == null) return;
- MetricsLogger.action(mContext, MetricsEvent.QS_CAST_DISCONNECT);
- final CastDevice device = (CastDevice) item.tag;
- mController.stopCasting(device);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 698a253..e116f75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -32,9 +32,7 @@
import android.telephony.SubscriptionManager;
import android.text.Html;
import android.text.TextUtils;
-import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.view.WindowManager.LayoutParams;
import android.widget.Switch;
@@ -49,7 +47,6 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile.SignalState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -71,7 +68,6 @@
private final NetworkController mController;
private final DataUsageController mDataController;
- private final CellularDetailAdapter mDetailAdapter;
private final CellSignalCallback mSignalCallback = new CellSignalCallback();
@@ -91,7 +87,6 @@
statusBarStateController, activityStarter, qsLogger);
mController = networkController;
mDataController = mController.getMobileDataController();
- mDetailAdapter = new CellularDetailAdapter();
mController.observe(getLifecycle(), mSignalCallback);
}
@@ -106,11 +101,6 @@
}
@Override
- public DetailAdapter getDetailAdapter() {
- return mDetailAdapter;
- }
-
- @Override
public Intent getLongClickIntent() {
if (getState().state == Tile.STATE_UNAVAILABLE) {
return new Intent(Settings.ACTION_WIRELESS_SETTINGS);
@@ -161,12 +151,7 @@
@Override
protected void handleSecondaryClick(@Nullable View view) {
- if (mDataController.isMobileDataSupported()) {
- showDetail(true);
- } else {
- mActivityStarter
- .postStartActivityDismissingKeyguard(getCellularSettingIntent(),0 /* delay */);
- }
+ handleLongClick(view);
}
@Override
@@ -298,11 +283,6 @@
mInfo.airplaneModeEnabled = icon.visible;
refreshState(mInfo);
}
-
- @Override
- public void setMobileDataEnabled(boolean enabled) {
- mDetailAdapter.setMobileDataEnabled(enabled);
- }
}
static Intent getCellularSettingIntent() {
@@ -314,53 +294,4 @@
}
return intent;
}
-
- private final class CellularDetailAdapter implements DetailAdapter {
-
- @Override
- public CharSequence getTitle() {
- return mContext.getString(R.string.quick_settings_cellular_detail_title);
- }
-
- @Nullable
- @Override
- public Boolean getToggleState() {
- return mDataController.isMobileDataSupported()
- ? mDataController.isMobileDataEnabled()
- : null;
- }
-
- @Override
- public Intent getSettingsIntent() {
- return getCellularSettingIntent();
- }
-
- @Override
- public void setToggleState(boolean state) {
- MetricsLogger.action(mContext, MetricsEvent.QS_CELLULAR_TOGGLE, state);
- mDataController.setMobileDataEnabled(state);
- }
-
- @Override
- public int getMetricsCategory() {
- return MetricsEvent.QS_DATAUSAGEDETAIL;
- }
-
- @Override
- public View createDetailView(Context context, View convertView, ViewGroup parent) {
- final DataUsageDetailView v = (DataUsageDetailView) (convertView != null
- ? convertView
- : LayoutInflater.from(mContext).inflate(R.layout.data_usage, parent, false));
- final DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
- if (info == null) return v;
- v.bind(info);
- v.findViewById(R.id.roaming_text).setVisibility(mSignalCallback.mInfo.roaming
- ? View.VISIBLE : View.INVISIBLE);
- return v;
- }
-
- public void setMobileDataEnabled(boolean enabled) {
- fireToggleStateChanged(enabled);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index a33650c..6cff4cd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -25,8 +25,6 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
@@ -34,16 +32,10 @@
import android.provider.Settings;
import android.provider.Settings.Global;
import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.ZenRule;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
-import android.util.Slog;
-import android.view.LayoutInflater;
import android.view.View;
-import android.view.View.OnAttachStateChangeListener;
-import android.view.ViewGroup;
import android.widget.Switch;
-import android.widget.Toast;
import androidx.annotation.Nullable;
@@ -52,13 +44,11 @@
import com.android.settingslib.notification.EnableZenModeDialog;
import com.android.systemui.Prefs;
import com.android.systemui.R;
-import com.android.systemui.SysUIToast;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -69,7 +59,6 @@
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.settings.SecureSettings;
-import com.android.systemui.volume.ZenModePanel;
import javax.inject.Inject;
@@ -83,14 +72,12 @@
new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS);
private final ZenModeController mController;
- private final DndDetailAdapter mDetailAdapter;
private final SharedPreferences mSharedPreferences;
private final SettingObserver mSettingZenDuration;
private final DialogLaunchAnimator mDialogLaunchAnimator;
private final QSZenModeDialogMetricsLogger mQSZenDialogMetricsLogger;
private boolean mListening;
- private boolean mShowingDetail;
@Inject
public DndTile(
@@ -111,7 +98,6 @@
statusBarStateController, activityStarter, qsLogger);
mController = zenModeController;
mSharedPreferences = sharedPreferences;
- mDetailAdapter = new DndDetailAdapter();
mController.observe(getLifecycle(), mZenCallback);
mDialogLaunchAnimator = dialogLaunchAnimator;
mSettingZenDuration = new SettingObserver(secureSettings, mUiHandler,
@@ -142,11 +128,6 @@
}
@Override
- public DetailAdapter getDetailAdapter() {
- return mDetailAdapter;
- }
-
- @Override
public BooleanState newTileState() {
return new BooleanState();
}
@@ -225,28 +206,7 @@
@Override
protected void handleSecondaryClick(@Nullable View view) {
- if (mController.isVolumeRestricted()) {
- // Collapse the panels, so the user can see the toast.
- mHost.collapsePanels();
- SysUIToast.makeText(mContext, mContext.getString(
- com.android.internal.R.string.error_message_change_not_allowed),
- Toast.LENGTH_LONG).show();
- return;
- }
- if (!mState.value) {
- // Because of the complexity of the zen panel, it needs to be shown after
- // we turn on zen below.
- mController.addCallback(new ZenModeController.Callback() {
- @Override
- public void onZenChanged(int zen) {
- mController.removeCallback(this);
- showDetail(true);
- }
- });
- mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
- } else {
- showDetail(true);
- }
+ handleLongClick(view);
}
@Override
@@ -295,9 +255,6 @@
R.string.accessibility_quick_settings_dnd);
break;
}
- if (valueChanged) {
- fireToggleStateChanged(state.value);
- }
state.dualLabelContentDescription = mContext.getResources().getString(
R.string.accessibility_quick_settings_open_settings, getTileLabel());
state.expandedAccessibilityClassName = Switch.class.getName();
@@ -349,146 +306,6 @@
private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
public void onZenChanged(int zen) {
refreshState(zen);
- if (isShowingDetail()) {
- mDetailAdapter.updatePanel();
- }
- }
-
- @Override
- public void onConfigChanged(ZenModeConfig config) {
- if (isShowingDetail()) {
- mDetailAdapter.updatePanel();
- }
- }
- };
-
- private final class DndDetailAdapter implements DetailAdapter, OnAttachStateChangeListener {
-
- @Nullable
- private ZenModePanel mZenPanel;
- private boolean mAuto;
-
- @Override
- public CharSequence getTitle() {
- return mContext.getString(R.string.quick_settings_dnd_label);
- }
-
- @Override
- public Boolean getToggleState() {
- return mState.value;
- }
-
- @Override
- public Intent getSettingsIntent() {
- return ZEN_SETTINGS;
- }
-
- @Override
- public void setToggleState(boolean state) {
- MetricsLogger.action(mContext, MetricsEvent.QS_DND_TOGGLE, state);
- if (!state) {
- mController.setZen(ZEN_MODE_OFF, null, TAG);
- mAuto = false;
- } else {
- mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
- }
- }
-
- @Override
- public int getMetricsCategory() {
- return MetricsEvent.QS_DND_DETAILS;
- }
-
- @Override
- public View createDetailView(Context context, View convertView, ViewGroup parent) {
- mZenPanel = convertView != null ? (ZenModePanel) convertView
- : (ZenModePanel) LayoutInflater.from(context).inflate(
- R.layout.zen_mode_panel, parent, false);
- if (convertView == null) {
- mZenPanel.init(mController);
- mZenPanel.addOnAttachStateChangeListener(this);
- mZenPanel.setCallback(mZenModePanelCallback);
- mZenPanel.setEmptyState(R.drawable.ic_qs_dnd_detail_empty, R.string.dnd_is_off);
- }
- updatePanel();
- return mZenPanel;
- }
-
- private void updatePanel() {
- if (mZenPanel == null) return;
- mAuto = false;
- if (mController.getZen() == ZEN_MODE_OFF) {
- mZenPanel.setState(ZenModePanel.STATE_OFF);
- } else {
- ZenModeConfig config = mController.getConfig();
- String summary = "";
- if (config.manualRule != null && config.manualRule.enabler != null) {
- summary = getOwnerCaption(config.manualRule.enabler);
- }
- for (ZenRule automaticRule : config.automaticRules.values()) {
- if (automaticRule.isAutomaticActive()) {
- if (summary.isEmpty()) {
- summary = mContext.getString(R.string.qs_dnd_prompt_auto_rule,
- automaticRule.name);
- } else {
- summary = mContext.getString(R.string.qs_dnd_prompt_auto_rule_app);
- }
- }
- }
- if (summary.isEmpty()) {
- mZenPanel.setState(ZenModePanel.STATE_MODIFY);
- } else {
- mAuto = true;
- mZenPanel.setState(ZenModePanel.STATE_AUTO_RULE);
- mZenPanel.setAutoText(summary);
- }
- }
- }
-
- private String getOwnerCaption(String owner) {
- final PackageManager pm = mContext.getPackageManager();
- try {
- final ApplicationInfo info = pm.getApplicationInfo(owner, 0);
- if (info != null) {
- final CharSequence seq = info.loadLabel(pm);
- if (seq != null) {
- final String str = seq.toString().trim();
- return mContext.getString(R.string.qs_dnd_prompt_app, str);
- }
- }
- } catch (Throwable e) {
- Slog.w(TAG, "Error loading owner caption", e);
- }
- return "";
- }
-
- @Override
- public void onViewAttachedToWindow(View v) {
- mShowingDetail = true;
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- mShowingDetail = false;
- mZenPanel = null;
- }
- }
-
- private final ZenModePanel.Callback mZenModePanelCallback = new ZenModePanel.Callback() {
- @Override
- public void onPrioritySettings() {
- mActivityStarter.postStartActivityDismissingKeyguard(
- ZEN_PRIORITY_SETTINGS, 0);
- }
-
- @Override
- public void onInteraction() {
- // noop
- }
-
- @Override
- public void onExpanded(boolean expanded) {
- // noop
}
};
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 9df942d..cd7021e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -411,10 +411,6 @@
}
boolean wifiConnected = cb.mEnabled && (cb.mWifiSignalIconId > 0) && (cb.mSsid != null);
boolean wifiNotConnected = (cb.mWifiSignalIconId > 0) && (cb.mSsid == null);
- boolean enabledChanging = state.value != cb.mEnabled;
- if (enabledChanging) {
- fireToggleStateChanged(cb.mEnabled);
- }
if (state.slash == null) {
state.slash = new SlashState();
state.slash.rotation = 6;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index 0be0619..f4dd415 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -36,7 +36,6 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -134,12 +133,6 @@
return new Intent(Settings.ACTION_PRIVACY_SETTINGS);
}
- @Nullable
- @Override
- public DetailAdapter getDetailAdapter() {
- return super.getDetailAdapter();
- }
-
@Override
public void onSensorBlockedChanged(int sensor, boolean blocked) {
if (sensor == getSensorId()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index 076ef35..5840a3d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -150,6 +150,6 @@
}
protected int getFontSizeDimen() {
- return R.dimen.qs_detail_item_secondary_text_size;
+ return R.dimen.qs_tile_text_size;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
deleted file mode 100644
index db1b6e6..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2015 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.qs.tiles;
-
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.Settings;
-import android.util.Pair;
-import android.view.View;
-
-import androidx.annotation.Nullable;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
-import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTile.State;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-
-import javax.inject.Inject;
-
-public class UserTile extends QSTileImpl<State> implements UserInfoController.OnUserInfoChangedListener {
-
- private final UserSwitcherController mUserSwitcherController;
- private final UserInfoController mUserInfoController;
- @Nullable
- private Pair<String, Drawable> mLastUpdate;
-
- @Inject
- public UserTile(
- QSHost host,
- @Background Looper backgroundLooper,
- @Main Handler mainHandler,
- FalsingManager falsingManager,
- MetricsLogger metricsLogger,
- StatusBarStateController statusBarStateController,
- ActivityStarter activityStarter,
- QSLogger qsLogger,
- UserSwitcherController userSwitcherController,
- UserInfoController userInfoController
- ) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
- statusBarStateController, activityStarter, qsLogger);
- mUserSwitcherController = userSwitcherController;
- mUserInfoController = userInfoController;
- mUserInfoController.observe(getLifecycle(), this);
- }
-
- @Override
- public State newTileState() {
- return new QSTile.State();
- }
-
- @Override
- public Intent getLongClickIntent() {
- return new Intent(Settings.ACTION_USER_SETTINGS);
- }
-
- @Override
- protected void handleClick(@Nullable View view) {
- showDetail(true);
- }
-
- @Override
- public DetailAdapter getDetailAdapter() {
- return mUserSwitcherController.mUserDetailAdapter;
- }
-
- @Override
- public int getMetricsCategory() {
- return MetricsEvent.QS_USER_TILE;
- }
-
- @Override
- public CharSequence getTileLabel() {
- return getState().label;
- }
-
- @Override
- protected void handleUpdateState(State state, Object arg) {
- final Pair<String, Drawable> p = arg != null ? (Pair<String, Drawable>) arg : mLastUpdate;
- if (p != null) {
- state.label = p.first;
- // TODO: Better content description.
- state.contentDescription = p.first;
- state.icon = new Icon() {
- @Override
- public Drawable getDrawable(Context context) {
- return p.second;
- }
- };
- } else {
- // TODO: Default state.
- }
- }
-
- @Override
- public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
- mLastUpdate = new Pair<>(name, picture);
- refreshState(mLastUpdate);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index c82ff34..b2be56cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -28,27 +28,22 @@
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.Switch;
import androidx.annotation.Nullable;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.wifi.AccessPoint;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.SignalState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.AlphaControlledSignalTileView;
-import com.android.systemui.qs.QSDetailItems;
-import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
@@ -58,9 +53,6 @@
import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.connectivity.WifiIcons;
import com.android.systemui.statusbar.connectivity.WifiIndicators;
-import com.android.wifitrackerlib.WifiEntry;
-
-import java.util.List;
import javax.inject.Inject;
@@ -70,7 +62,6 @@
protected final NetworkController mController;
private final AccessPointController mWifiController;
- private final WifiDetailAdapter mDetailAdapter;
private final QSTile.SignalState mStateBeforeClick = newTileState();
protected final WifiSignalCallback mSignalCallback = new WifiSignalCallback();
@@ -93,7 +84,6 @@
statusBarStateController, activityStarter, qsLogger);
mController = networkController;
mWifiController = accessPointController;
- mDetailAdapter = (WifiDetailAdapter) createDetailAdapter();
mController.observe(getLifecycle(), mSignalCallback);
mStateBeforeClick.spec = "wifi";
}
@@ -104,25 +94,6 @@
}
@Override
- public void setDetailListening(boolean listening) {
- if (listening) {
- mWifiController.addAccessPointCallback(mDetailAdapter);
- } else {
- mWifiController.removeAccessPointCallback(mDetailAdapter);
- }
- }
-
- @Override
- public DetailAdapter getDetailAdapter() {
- return mDetailAdapter;
- }
-
- @Override
- protected DetailAdapter createDetailAdapter() {
- return new WifiDetailAdapter();
- }
-
- @Override
public QSIconView createTileView(Context context) {
return new AlphaControlledSignalTileView(context);
}
@@ -158,7 +129,6 @@
new Intent(Settings.ACTION_WIFI_SETTINGS), 0);
return;
}
- showDetail(true);
if (!mState.value) {
mController.setWifiEnabled(true);
}
@@ -185,11 +155,6 @@
&& (cb.ssid != null || cb.wifiSignalIconId != WifiIcons.QS_WIFI_NO_NETWORK);
boolean wifiNotConnected = (cb.ssid == null)
&& (cb.wifiSignalIconId == WifiIcons.QS_WIFI_NO_NETWORK);
- boolean enabledChanging = state.value != cb.enabled;
- if (enabledChanging) {
- mDetailAdapter.setItemsVisible(cb.enabled);
- fireToggleStateChanged(cb.enabled);
- }
if (state.slash == null) {
state.slash = new SlashState();
state.slash.rotation = 6;
@@ -315,150 +280,7 @@
mInfo.wifiSignalContentDescription = indicators.qsIcon.contentDescription;
mInfo.isTransient = indicators.isTransient;
mInfo.statusLabel = indicators.statusLabel;
- if (isShowingDetail()) {
- mDetailAdapter.updateItems();
- }
refreshState();
}
}
-
- protected class WifiDetailAdapter implements DetailAdapter,
- AccessPointController.AccessPointCallback, QSDetailItems.Callback {
-
- @Nullable
- private QSDetailItems mItems;
- @Nullable
- private WifiEntry[] mAccessPoints;
-
- @Override
- public CharSequence getTitle() {
- return mContext.getString(R.string.quick_settings_wifi_label);
- }
-
- public Intent getSettingsIntent() {
- return WIFI_SETTINGS;
- }
-
- @Override
- public Boolean getToggleState() {
- return mState.value;
- }
-
- @Override
- public void setToggleState(boolean state) {
- if (DEBUG) Log.d(TAG, "setToggleState " + state);
- MetricsLogger.action(mContext, MetricsEvent.QS_WIFI_TOGGLE, state);
- mController.setWifiEnabled(state);
- }
-
- @Override
- public int getMetricsCategory() {
- return MetricsEvent.QS_WIFI_DETAILS;
- }
-
- @Override
- public View createDetailView(Context context, View convertView, ViewGroup parent) {
- if (DEBUG) Log.d(TAG, "createDetailView convertView=" + (convertView != null));
- mAccessPoints = null;
- mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
- mItems.setTagSuffix("Wifi");
- mItems.setCallback(this);
- mWifiController.scanForAccessPoints(); // updates APs and items
- setItemsVisible(mState.value);
- return mItems;
- }
-
- @Override
- public void onAccessPointsChanged(final List<WifiEntry> accessPoints) {
- mAccessPoints = filterUnreachableAPs(accessPoints);
-
- updateItems();
- }
-
- /** Filter unreachable APs from mAccessPoints */
- private WifiEntry[] filterUnreachableAPs(List<WifiEntry> unfiltered) {
- int numReachable = 0;
- for (WifiEntry ap : unfiltered) {
- if (isWifiEntryReachable(ap)) numReachable++;
- }
- if (numReachable != unfiltered.size()) {
- WifiEntry[] accessPoints = new WifiEntry[numReachable];
- int i = 0;
- for (WifiEntry ap : unfiltered) {
- if (isWifiEntryReachable(ap)) accessPoints[i++] = ap;
- }
- return accessPoints;
- }
- return unfiltered.toArray(new WifiEntry[0]);
- }
-
- @Override
- public void onSettingsActivityTriggered(Intent settingsIntent) {
- mActivityStarter.postStartActivityDismissingKeyguard(settingsIntent, 0);
- }
-
- @Override
- public void onDetailItemClick(Item item) {
- if (item == null || item.tag == null) return;
- final WifiEntry ap = (WifiEntry) item.tag;
- if (ap.getConnectedState() == WifiEntry.CONNECTED_STATE_DISCONNECTED) {
- if (mWifiController.connect(ap)) {
- mHost.collapsePanels();
- }
- }
- showDetail(false);
- }
-
- @Override
- public void onDetailItemDisconnect(Item item) {
- // noop
- }
-
- public void setItemsVisible(boolean visible) {
- if (mItems == null) return;
- mItems.setItemsVisible(visible);
- }
-
- private void updateItems() {
- if (mItems == null) return;
- if ((mAccessPoints != null && mAccessPoints.length > 0)
- || !mSignalCallback.mInfo.enabled) {
- fireScanStateChanged(false);
- } else {
- fireScanStateChanged(true);
- }
-
- // Wi-Fi is off
- if (!mSignalCallback.mInfo.enabled) {
- mItems.setEmptyState(WifiIcons.QS_WIFI_NO_NETWORK,
- R.string.wifi_is_off);
- mItems.setItems(null);
- return;
- }
-
- // No available access points
- mItems.setEmptyState(WifiIcons.QS_WIFI_NO_NETWORK,
- R.string.quick_settings_wifi_detail_empty_text);
-
- // Build the list
- Item[] items = null;
- if (mAccessPoints != null) {
- items = new Item[mAccessPoints.length];
- for (int i = 0; i < mAccessPoints.length; i++) {
- final WifiEntry ap = mAccessPoints[i];
- final Item item = new Item(mWifiController.getIcon(ap), ap.getSsid(), ap);
- item.line2 = ap.getSummary();
- item.icon2 = ap.getSecurity() != AccessPoint.SECURITY_NONE
- ? R.drawable.qs_ic_wifi_lock
- : -1;
- items[i] = item;
- }
- }
- mItems.setItems(items);
- }
- }
-
- private static boolean isWifiEntryReachable(WifiEntry ap) {
- return ap.getLevel() != WifiEntry.WIFI_LEVEL_UNREACHABLE;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
index 7c8f4b15..8c8c5c8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -24,11 +24,13 @@
import android.view.LayoutInflater
import android.view.View
import androidx.annotation.VisibleForTesting
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.R
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.QSUserSwitcherEvent
import com.android.systemui.qs.tiles.UserDetailView
import com.android.systemui.statusbar.phone.SystemUIDialog
import javax.inject.Inject
@@ -43,6 +45,7 @@
private val activityStarter: ActivityStarter,
private val falsingManager: FalsingManager,
private val dialogLaunchAnimator: DialogLaunchAnimator,
+ private val uiEventLogger: UiEventLogger,
private val dialogFactory: (Context) -> SystemUIDialog
) {
@@ -51,12 +54,14 @@
userDetailViewAdapterProvider: Provider<UserDetailView.Adapter>,
activityStarter: ActivityStarter,
falsingManager: FalsingManager,
- dialogLaunchAnimator: DialogLaunchAnimator
+ dialogLaunchAnimator: DialogLaunchAnimator,
+ uiEventLogger: UiEventLogger
) : this(
userDetailViewAdapterProvider,
activityStarter,
falsingManager,
dialogLaunchAnimator,
+ uiEventLogger,
{ SystemUIDialog(it) }
)
@@ -76,10 +81,13 @@
setCanceledOnTouchOutside(true)
setTitle(R.string.qs_user_switch_dialog_title)
- setPositiveButton(R.string.quick_settings_done, null)
+ setPositiveButton(R.string.quick_settings_done) { _, _ ->
+ uiEventLogger.log(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE)
+ }
setNeutralButton(R.string.quick_settings_more_user_settings) { _, _ ->
if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
+ uiEventLogger.log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
activityStarter.postStartActivityDismissingKeyguard(
USER_SETTINGS_INTENT,
0
@@ -95,6 +103,7 @@
adapter.linkToViewGroup(gridFrame.findViewById(R.id.grid))
dialogLaunchAnimator.showFromView(this, view)
+ uiEventLogger.log(QSUserSwitcherEvent.QS_USER_DETAIL_OPEN)
adapter.injectDialogShower(DialogShowerImpl(this, dialogLaunchAnimator))
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 30456a8..50765f2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -51,7 +51,9 @@
import android.graphics.Insets;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
-import android.media.MediaActionSound;
+import android.media.AudioAttributes;
+import android.media.AudioSystem;
+import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
@@ -93,6 +95,7 @@
import com.google.common.util.concurrent.ListenableFuture;
+import java.io.File;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
@@ -248,7 +251,7 @@
private final WindowManager mWindowManager;
private final WindowManager.LayoutParams mWindowLayoutParams;
private final AccessibilityManager mAccessibilityManager;
- private final MediaActionSound mCameraSound;
+ private final MediaPlayer mCameraSound;
private final ScrollCaptureClient mScrollCaptureClient;
private final PhoneWindow mWindow;
private final DisplayManager mDisplayManager;
@@ -331,8 +334,13 @@
reloadAssets();
// Setup the Camera shutter sound
- mCameraSound = new MediaActionSound();
- mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
+ mCameraSound = MediaPlayer.create(mContext,
+ Uri.fromFile(new File(mContext.getResources().getString(
+ com.android.internal.R.string.config_cameraShutterSound))), null,
+ new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .build(), AudioSystem.newAudioSessionId());
mCopyBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -430,7 +438,9 @@
void releaseContext() {
mContext.unregisterReceiver(mCopyBroadcastReceiver);
mContext.release();
- mCameraSound.release();
+ if (mCameraSound != null) {
+ mCameraSound.release();
+ }
mBgExecutor.shutdownNow();
}
@@ -806,7 +816,9 @@
*/
private void saveScreenshotAndToast(Consumer<Uri> finisher) {
// Play the shutter sound to notify that we've taken a screenshot
- mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
+ if (mCameraSound != null) {
+ mCameraSound.start();
+ }
saveScreenshotInWorkerThread(
/* onComplete */ finisher,
@@ -840,7 +852,9 @@
mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash);
// Play the shutter sound to notify that we've taken a screenshot
- mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
+ if (mCameraSound != null) {
+ mCameraSound.start();
+ }
if (DEBUG_ANIM) {
Log.d(TAG, "starting post-screenshot animation");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index e359968..72c4ce8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -37,6 +37,7 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Parcelable;
+import android.os.Trace;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
@@ -371,10 +372,13 @@
}
Drawable drawable;
try {
+ Trace.beginSection("StatusBarIconView#updateDrawable()");
drawable = getIcon(mIcon);
} catch (OutOfMemoryError e) {
Log.w(TAG, "OOM while inflating " + mIcon.icon + " for slot " + mSlot);
return false;
+ } finally {
+ Trace.endSection();
}
if (drawable == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
index 357a12b..324d47e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
@@ -29,9 +29,7 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.qs.FooterActionsView;
-import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.user.UserSwitchDialogController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -44,7 +42,6 @@
public class MultiUserSwitchController extends ViewController<MultiUserSwitch> {
private final UserManager mUserManager;
private final UserSwitcherController mUserSwitcherController;
- private final QSDetailDisplayer mQsDetailDisplayer;
private final FalsingManager mFalsingManager;
private final UserSwitchDialogController mUserSwitchDialogController;
private final ActivityStarter mActivityStarter;
@@ -66,17 +63,8 @@
mActivityStarter.startActivity(intent, true /* dismissShade */,
ActivityLaunchAnimator.Controller.fromView(v, null),
true /* showOverlockscreenwhenlocked */);
- } else if (mFeatureFlags.isEnabled(Flags.NEW_USER_SWITCHER)) {
- mUserSwitchDialogController.showDialog(v);
} else {
- View center = mView.getChildCount() > 0 ? mView.getChildAt(0) : mView;
-
- int[] tmpInt = new int[2];
- center.getLocationInWindow(tmpInt);
- tmpInt[0] += center.getWidth() / 2;
- tmpInt[1] += center.getHeight() / 2;
-
- mQsDetailDisplayer.showDetailAdapter(getUserDetailAdapter(), tmpInt[0], tmpInt[1]);
+ mUserSwitchDialogController.showDialog(v);
}
}
};
@@ -85,7 +73,6 @@
public static class Factory {
private final UserManager mUserManager;
private final UserSwitcherController mUserSwitcherController;
- private final QSDetailDisplayer mQsDetailDisplayer;
private final FalsingManager mFalsingManager;
private final UserSwitchDialogController mUserSwitchDialogController;
private final ActivityStarter mActivityStarter;
@@ -93,12 +80,11 @@
@Inject
public Factory(UserManager userManager, UserSwitcherController userSwitcherController,
- QSDetailDisplayer qsDetailDisplayer, FalsingManager falsingManager,
+ FalsingManager falsingManager,
UserSwitchDialogController userSwitchDialogController, FeatureFlags featureFlags,
ActivityStarter activityStarter) {
mUserManager = userManager;
mUserSwitcherController = userSwitcherController;
- mQsDetailDisplayer = qsDetailDisplayer;
mFalsingManager = falsingManager;
mUserSwitchDialogController = userSwitchDialogController;
mActivityStarter = activityStarter;
@@ -107,20 +93,19 @@
public MultiUserSwitchController create(FooterActionsView view) {
return new MultiUserSwitchController(view.findViewById(R.id.multi_user_switch),
- mUserManager, mUserSwitcherController, mQsDetailDisplayer,
+ mUserManager, mUserSwitcherController,
mFalsingManager, mUserSwitchDialogController, mFeatureFlags,
mActivityStarter);
}
}
private MultiUserSwitchController(MultiUserSwitch view, UserManager userManager,
- UserSwitcherController userSwitcherController, QSDetailDisplayer qsDetailDisplayer,
+ UserSwitcherController userSwitcherController,
FalsingManager falsingManager, UserSwitchDialogController userSwitchDialogController,
FeatureFlags featureFlags, ActivityStarter activityStarter) {
super(view);
mUserManager = userManager;
mUserSwitcherController = userSwitcherController;
- mQsDetailDisplayer = qsDetailDisplayer;
mFalsingManager = falsingManager;
mUserSwitchDialogController = userSwitchDialogController;
mFeatureFlags = featureFlags;
@@ -143,10 +128,6 @@
mView.setOnClickListener(null);
}
- protected DetailAdapter getUserDetailAdapter() {
- return mUserSwitcherController.mUserDetailAdapter;
- }
-
private void registerListener() {
if (mUserManager.isUserSwitcherEnabled() && mUserListener == null) {
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 7a2eceb..d93c013 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -146,12 +146,10 @@
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.FalsingManager.FalsingTapListener;
-import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
-import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
@@ -325,7 +323,6 @@
private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
private final IdleViewComponent.Factory mIdleViewComponentFactory;
- private final QSDetailDisplayer mQSDetailDisplayer;
private final FragmentService mFragmentService;
private final ScrimController mScrimController;
private final PrivacyDotViewController mPrivacyDotViewController;
@@ -773,7 +770,6 @@
CommunalViewComponent.Factory communalViewComponentFactory,
IdleViewComponent.Factory idleViewComponentFactory,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
- QSDetailDisplayer qsDetailDisplayer,
NotificationGroupManagerLegacy groupManager,
NotificationIconAreaController notificationIconAreaController,
AuthController authController,
@@ -848,7 +844,6 @@
mContentResolver = contentResolver;
mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory;
mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory;
- mQSDetailDisplayer = qsDetailDisplayer;
mFragmentService = fragmentService;
mSettingsChangeObserver = new SettingsChangeObserver(handler);
mShouldUseSplitNotificationShade =
@@ -1136,7 +1131,6 @@
mKeyguardQsUserSwitchComponentFactory.build(userAvatarView);
mKeyguardQsUserSwitchController =
userSwitcherComponent.getKeyguardQsUserSwitchController();
- mKeyguardQsUserSwitchController.setNotificationPanelViewController(this);
mKeyguardQsUserSwitchController.init();
mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true);
} else if (keyguardUserSwitcherView != null) {
@@ -1848,18 +1842,6 @@
}
}
- public void expandWithQsDetail(DetailAdapter qsDetailAdapter) {
- traceQsJank(true /* startTracing */, false /* wasCancelled */);
- flingSettings(0 /* velocity */, FLING_EXPAND);
- // When expanding with a panel, there's no meaningful touch point to correspond to. Set the
- // origin to somewhere above the screen. This is used for animations.
- int x = mQsFrame.getWidth() / 2;
- mQSDetailDisplayer.showDetailAdapter(qsDetailAdapter, x, -getHeight());
- if (mAccessibilityManager.isEnabled()) {
- mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
- }
- }
-
public void expandWithoutQs() {
if (isQsExpanded()) {
flingSettings(0 /* velocity */, FLING_COLLAPSE);
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 716e8c7..f13334e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1110,6 +1110,12 @@
}
private void onFoldedStateChanged(boolean isFolded, boolean willGoToSleep) {
+ Trace.beginSection("StatusBar#onFoldedStateChanged");
+ onFoldedStateChangedInternal(isFolded, willGoToSleep);
+ Trace.endSection();
+ }
+
+ private void onFoldedStateChangedInternal(boolean isFolded, boolean willGoToSleep) {
// Folded state changes are followed by a screen off event.
// By default turning off the screen also closes the shade.
// We want to make sure that the shade status is kept after
@@ -3672,6 +3678,7 @@
@Override
public void onScreenTurnedOff() {
+ Trace.beginSection("StatusBar#onScreenTurnedOff");
mFalsingCollector.onScreenOff();
mScrimController.onScreenTurnedOff();
if (mCloseQsBeforeScreenOff) {
@@ -3679,6 +3686,7 @@
mCloseQsBeforeScreenOff = false;
}
updateIsKeyguard();
+ Trace.endSection();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
index cf9a5db..4081962 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -235,11 +235,6 @@
// Settings are not available in setup
if (!mDeviceProvisionedController.isCurrentUserSetup()) return;
-
- QSPanelController qsPanelController = mStatusBar.getQSPanelController();
- if (subPanel != null && qsPanelController != null) {
- qsPanelController.openDetails(subPanel);
- }
mNotificationPanelViewController.expandWithQs();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
index a124753..909261f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
@@ -16,9 +16,15 @@
package com.android.systemui.statusbar.phone.userswitcher
+import android.content.Intent
import android.view.View
+import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.user.UserSwitchDialogController
+import com.android.systemui.user.UserSwitcherActivity
import com.android.systemui.util.ViewController
import javax.inject.Inject
@@ -30,7 +36,9 @@
view: StatusBarUserSwitcherContainer,
private val tracker: StatusBarUserInfoTracker,
private val featureController: StatusBarUserSwitcherFeatureController,
- private val userSwitcherDialogController: UserSwitchDialogController
+ private val userSwitcherDialogController: UserSwitchDialogController,
+ private val featureFlags: FeatureFlags,
+ private val activityStarter: ActivityStarter
) : ViewController<StatusBarUserSwitcherContainer>(view),
StatusBarUserSwitcherController {
private val listener = object : CurrentUserChipInfoUpdatedListener {
@@ -52,8 +60,17 @@
override fun onViewAttached() {
tracker.addCallback(listener)
featureController.addCallback(featureFlagListener)
- mView.setOnClickListener {
- userSwitcherDialogController.showDialog(it)
+ mView.setOnClickListener { view: View ->
+ if (featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)) {
+ val intent = Intent(context, UserSwitcherActivity::class.java)
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
+
+ activityStarter.startActivity(intent, true /* dismissShade */,
+ ActivityLaunchAnimator.Controller.fromView(view, null),
+ true /* showOverlockscreenwhenlocked */)
+ } else {
+ userSwitcherDialogController.showDialog(view)
+ }
}
updateEnabled()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index d903639..1d414745 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -21,6 +21,7 @@
import android.annotation.Nullable;
import android.hardware.devicestate.DeviceStateManager;
+import android.os.Trace;
import android.util.Log;
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
@@ -117,11 +118,16 @@
private void updateDeviceState(int state) {
Log.v(TAG, "updateDeviceState [state=" + state + "]");
- if (mDeviceState == state) {
- return;
- }
+ Trace.beginSection("updateDeviceState [state=" + state + "]");
+ try {
+ if (mDeviceState == state) {
+ return;
+ }
- readPersistedSetting(state);
+ readPersistedSetting(state);
+ } finally {
+ Trace.endSection();
+ }
}
private void readPersistedSetting(int state) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index b591545..7e2488f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -37,12 +37,9 @@
import com.android.systemui.R;
import com.android.systemui.communal.CommunalStateController;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.tiles.UserDetailView;
import com.android.systemui.qs.user.UserSwitchDialogController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
@@ -51,13 +48,11 @@
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.UserAvatarView;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
-import javax.inject.Provider;
/**
* Manages the user switch on the Keyguard that is used for opening the QS user panel.
@@ -81,11 +76,8 @@
protected final SysuiStatusBarStateController mStatusBarStateController;
private final ConfigurationController mConfigurationController;
private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
- private final KeyguardUserDetailAdapter mUserDetailAdapter;
- private final FeatureFlags mFeatureFlags;
private final UserSwitchDialogController mUserSwitchDialogController;
private final UiEventLogger mUiEventLogger;
- private NotificationPanelViewController mNotificationPanelViewController;
private UserAvatarView mUserAvatarView;
UserSwitcherController.UserRecord mCurrentUser;
@@ -133,9 +125,7 @@
ConfigurationController configurationController,
SysuiStatusBarStateController statusBarStateController,
DozeParameters dozeParameters,
- Provider<UserDetailView.Adapter> userDetailViewAdapterProvider,
ScreenOffAnimationController screenOffAnimationController,
- FeatureFlags featureFlags,
UserSwitchDialogController userSwitchDialogController,
UiEventLogger uiEventLogger) {
super(view);
@@ -152,8 +142,6 @@
keyguardStateController, dozeParameters,
screenOffAnimationController, /* animateYPos= */ false,
/* visibleOnCommunal= */ false);
- mUserDetailAdapter = new KeyguardUserDetailAdapter(context, userDetailViewAdapterProvider);
- mFeatureFlags = featureFlags;
mUserSwitchDialogController = userSwitchDialogController;
mUiEventLogger = uiEventLogger;
}
@@ -182,11 +170,7 @@
mUiEventLogger.log(
LockscreenGestureLogger.LockscreenUiEvent.LOCKSCREEN_SWITCH_USER_TAP);
- if (mFeatureFlags.isEnabled(Flags.NEW_USER_SWITCHER)) {
- mUserSwitchDialogController.showDialog(mView);
- } else {
- openQsUserPanel();
- }
+ mUserSwitchDialogController.showDialog(mView);
});
mUserAvatarView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
@@ -331,40 +315,4 @@
private boolean isListAnimating() {
return mKeyguardVisibilityHelper.isVisibilityAnimating();
}
-
- private void openQsUserPanel() {
- mNotificationPanelViewController.expandWithQsDetail(mUserDetailAdapter);
- }
-
- public void setNotificationPanelViewController(
- NotificationPanelViewController notificationPanelViewController) {
- mNotificationPanelViewController = notificationPanelViewController;
- }
-
- class KeyguardUserDetailAdapter extends UserSwitcherController.UserDetailAdapter {
- KeyguardUserDetailAdapter(Context context,
- Provider<UserDetailView.Adapter> userDetailViewAdapterProvider) {
- super(context, userDetailViewAdapterProvider);
- }
-
- @Override
- public boolean shouldAnimate() {
- return false;
- }
-
- @Override
- public int getDoneText() {
- return R.string.quick_settings_close_user_panel;
- }
-
- @Override
- public boolean onDoneButtonClicked() {
- if (mNotificationPanelViewController != null) {
- mNotificationPanelViewController.animateCloseQs(true /* animateAway */);
- return true;
- } else {
- return false;
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 7a7af4d..e416ed1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -51,7 +51,6 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.View;
-import android.view.ViewGroup;
import android.view.WindowManagerGlobal;
import android.widget.BaseAdapter;
@@ -60,7 +59,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.systemui.Dumpable;
@@ -77,9 +75,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.qs.QSUserSwitcherEvent;
-import com.android.systemui.qs.tiles.UserDetailView;
import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -96,7 +92,6 @@
import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
-import javax.inject.Provider;
/**
* Keeps a list of all users on the device for user switching.
@@ -153,7 +148,6 @@
private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
private final UiEventLogger mUiEventLogger;
private final IActivityManager mActivityManager;
- public final DetailAdapter mUserDetailAdapter;
private final Executor mBgExecutor;
private final boolean mGuestUserAutoCreated;
private final AtomicBoolean mGuestIsResetting;
@@ -177,7 +171,6 @@
FalsingManager falsingManager,
TelephonyListenerManager telephonyListenerManager,
IActivityTaskManager activityTaskManager,
- UserDetailAdapter userDetailAdapter,
SecureSettings secureSettings,
@Background Executor bgExecutor,
InteractionJankMonitor interactionJankMonitor,
@@ -196,7 +189,6 @@
mLatencyTracker = latencyTracker;
mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(
this, mUserTracker, mUiEventLogger, secureSettings);
- mUserDetailAdapter = userDetailAdapter;
mBgExecutor = bgExecutor;
if (!UserManager.isGuestUserEphemeral()) {
mGuestResumeSessionReceiver.register(mBroadcastDispatcher);
@@ -1118,77 +1110,6 @@
}
}
- public static class UserDetailAdapter implements DetailAdapter {
- private final Intent USER_SETTINGS_INTENT = new Intent(Settings.ACTION_USER_SETTINGS);
-
- private final Context mContext;
- private final Provider<UserDetailView.Adapter> mUserDetailViewAdapterProvider;
-
- @Inject
- UserDetailAdapter(Context context,
- Provider<UserDetailView.Adapter> userDetailViewAdapterProvider) {
- mContext = context;
- mUserDetailViewAdapterProvider = userDetailViewAdapterProvider;
- }
-
- @Override
- public CharSequence getTitle() {
- return mContext.getString(R.string.quick_settings_user_title);
- }
-
- @Override
- public View createDetailView(Context context, View convertView, ViewGroup parent) {
- UserDetailView v;
- if (!(convertView instanceof UserDetailView)) {
- v = UserDetailView.inflate(context, parent, false);
- v.setAdapter(mUserDetailViewAdapterProvider.get());
- } else {
- v = (UserDetailView) convertView;
- }
- v.refreshAdapter();
- return v;
- }
-
- @Override
- public Intent getSettingsIntent() {
- return USER_SETTINGS_INTENT;
- }
-
- @Override
- public int getSettingsText() {
- return R.string.quick_settings_more_user_settings;
- }
-
- @Override
- public Boolean getToggleState() {
- return null;
- }
-
- @Override
- public void setToggleState(boolean state) {
- }
-
- @Override
- public int getMetricsCategory() {
- return MetricsEvent.QS_USERDETAIL;
- }
-
- @Override
- public UiEventLogger.UiEventEnum openDetailEvent() {
- return QSUserSwitcherEvent.QS_USER_DETAIL_OPEN;
- }
-
- @Override
- public UiEventLogger.UiEventEnum closeDetailEvent() {
- return QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE;
- }
-
- @Override
- public UiEventLogger.UiEventEnum moreSettingsEvent() {
- return QSUserSwitcherEvent.QS_USER_MORE_SETTINGS;
- }
- };
-
private final KeyguardStateController.Callback mCallback =
new KeyguardStateController.Callback() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java
deleted file mode 100644
index 4d95969..0000000
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2016 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.tuner;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.Intent;
-import android.provider.Settings;
-import android.provider.Settings.Global;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.Checkable;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.systemui.Prefs;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.volume.ZenModePanel;
-import com.android.systemui.volume.ZenModePanel.Callback;
-
-public class TunerZenModePanel extends LinearLayout implements OnClickListener {
- private static final String TAG = "TunerZenModePanel";
-
- private Callback mCallback;
- private ZenModePanel mZenModePanel;
- private View mHeaderSwitch;
- private int mZenMode;
- private ZenModeController mController;
- private View mButtons;
- private View mMoreSettings;
- private View mDone;
- private OnClickListener mDoneListener;
- private boolean mEditing;
-
- public TunerZenModePanel(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
-
- public void init(ZenModeController zenModeController) {
- mController = zenModeController;
- mHeaderSwitch = findViewById(R.id.tuner_zen_switch);
- mHeaderSwitch.setVisibility(View.VISIBLE);
- mHeaderSwitch.setOnClickListener(this);
- ((TextView) mHeaderSwitch.findViewById(android.R.id.title)).setText(
- R.string.quick_settings_dnd_label);
- mZenModePanel = (ZenModePanel) findViewById(R.id.zen_mode_panel);
- mZenModePanel.init(zenModeController);
- mButtons = findViewById(R.id.tuner_zen_buttons);
- mMoreSettings = mButtons.findViewById(android.R.id.button2);
- mMoreSettings.setOnClickListener(this);
- ((TextView) mMoreSettings).setText(R.string.quick_settings_more_settings);
- mDone = mButtons.findViewById(android.R.id.button1);
- mDone.setOnClickListener(this);
- ((TextView) mDone).setText(R.string.quick_settings_done);
- // Hide the resizing space because it causes issues in the volume panel.
- ViewGroup detail_header = findViewById(R.id.tuner_zen_switch);
- detail_header.getChildAt(0).setVisibility(View.GONE);
- // No background so it can blend with volume panel.
- findViewById(R.id.edit_container).setBackground(null);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mEditing = false;
- }
-
- public void setCallback(Callback zenPanelCallback) {
- mCallback = zenPanelCallback;
- mZenModePanel.setCallback(zenPanelCallback);
- }
-
- @Override
- public void onClick(View v) {
- if (v == mHeaderSwitch) {
- mEditing = true;
- if (mZenMode == Global.ZEN_MODE_OFF) {
- mZenMode = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN,
- Global.ZEN_MODE_ALARMS);
- mController.setZen(mZenMode, null, TAG);
- postUpdatePanel();
- } else {
- mZenMode = Global.ZEN_MODE_OFF;
- mController.setZen(Global.ZEN_MODE_OFF, null, TAG);
- postUpdatePanel();
- }
- } else if (v == mMoreSettings) {
- Intent intent = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getContext().startActivity(intent);
- } else if (v == mDone) {
- mEditing = false;
- setVisibility(View.GONE);
- mDoneListener.onClick(v);
- }
- }
-
- public boolean isEditing() {
- return mEditing;
- }
-
- public void setZenState(int zenMode) {
- mZenMode = zenMode;
- postUpdatePanel();
- }
-
- private void postUpdatePanel() {
- // The complicated structure from reusing the same ZenPanel has resulted in some
- // unstableness/flickering from callbacks coming in quickly. To solve this just
- // post the UI updates a little bit.
- removeCallbacks(mUpdate);
- postDelayed(mUpdate, 40);
- }
-
- public void setDoneListener(OnClickListener onClickListener) {
- mDoneListener = onClickListener;
- }
-
- private void updatePanel() {
- boolean zenOn = mZenMode != Global.ZEN_MODE_OFF;
- ((Checkable) mHeaderSwitch.findViewById(android.R.id.toggle)).setChecked(zenOn);
- mZenModePanel.setVisibility(zenOn ? View.VISIBLE : View.GONE);
- mButtons.setVisibility(zenOn ? View.VISIBLE : View.GONE);
- }
-
- private final Runnable mUpdate = new Runnable() {
- @Override
- public void run() {
- updatePanel();
- }
- };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java b/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java
index 0bbf56c..db35437e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java
@@ -36,6 +36,7 @@
private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
private boolean mIsConditionMet = false;
private boolean mStarted = false;
+ private boolean mOverriding = false;
/**
* Starts monitoring the condition.
@@ -48,6 +49,21 @@
protected abstract void stop();
/**
+ * Sets whether this condition's value overrides others in determining the overall state.
+ */
+ public void setOverriding(boolean overriding) {
+ mOverriding = overriding;
+ updateCondition(mIsConditionMet);
+ }
+
+ /**
+ * Returns whether the current condition overrides
+ */
+ public boolean isOverridingCondition() {
+ return mOverriding;
+ }
+
+ /**
* Registers a callback to receive updates once started. This should be called before
* {@link #start()}. Also triggers the callback immediately if already started.
*/
@@ -57,7 +73,7 @@
mCallbacks.add(new WeakReference<>(callback));
if (mStarted) {
- callback.onConditionChanged(this, mIsConditionMet);
+ callback.onConditionChanged(this);
return;
}
@@ -107,11 +123,15 @@
if (cb == null) {
iterator.remove();
} else {
- cb.onConditionChanged(this, mIsConditionMet);
+ cb.onConditionChanged(this);
}
}
}
+ public boolean isConditionMet() {
+ return mIsConditionMet;
+ }
+
private boolean shouldLog() {
return Log.isLoggable(mTag, Log.DEBUG);
}
@@ -124,8 +144,7 @@
* Called when the fulfillment of the condition changes.
*
* @param condition The condition in question.
- * @param isConditionMet True if the condition has been fulfilled. False otherwise.
*/
- void onConditionChanged(Condition condition, boolean isConditionMet);
+ void onConditionChanged(Condition condition);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
index 8b6e982..7f3d54d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
@@ -18,15 +18,17 @@
import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.statusbar.policy.CallbackController;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collection;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
import javax.inject.Inject;
@@ -41,9 +43,7 @@
// Set of all conditions that need to be monitored.
private final Set<Condition> mConditions;
-
- // Map of values of each condition.
- private final HashMap<Condition, Boolean> mConditionsMap = new HashMap<>();
+ private final Executor mExecutor;
// Whether all conditions have been met.
private boolean mAllConditionsMet = false;
@@ -52,10 +52,43 @@
private boolean mHaveConditionsStarted = false;
// Callback for when each condition has been updated.
- private final Condition.Callback mConditionCallback = (condition, isConditionMet) -> {
- mConditionsMap.put(condition, isConditionMet);
+ private final Condition.Callback mConditionCallback = new Condition.Callback() {
+ @Override
+ public void onConditionChanged(Condition condition) {
+ mExecutor.execute(() -> updateConditionMetState());
+ }
+ };
- final boolean newAllConditionsMet = !mConditionsMap.containsValue(false);
+ @Inject
+ public Monitor(Executor executor, Set<Condition> conditions, Set<Callback> callbacks) {
+ mConditions = new HashSet<>();
+ mExecutor = executor;
+
+ if (conditions != null) {
+ mConditions.addAll(conditions);
+ }
+
+ if (callbacks == null) {
+ return;
+ }
+
+ for (Callback callback : callbacks) {
+ addCallbackLocked(callback);
+ }
+ }
+
+ private void updateConditionMetState() {
+ // Overriding conditions do not override each other
+ final Collection<Condition> overridingConditions = mConditions.stream()
+ .filter(Condition::isOverridingCondition).collect(Collectors.toSet());
+
+ final Collection<Condition> targetCollection = overridingConditions.isEmpty()
+ ? mConditions : overridingConditions;
+
+ final boolean newAllConditionsMet = targetCollection.isEmpty() ? true : targetCollection
+ .stream()
+ .map(Condition::isConditionMet)
+ .allMatch(conditionMet -> conditionMet);
if (newAllConditionsMet == mAllConditionsMet) {
return;
@@ -74,32 +107,44 @@
callback.onConditionsChanged(mAllConditionsMet);
}
}
- };
-
- @Inject
- public Monitor(Set<Condition> conditions, Set<Callback> callbacks) {
- mConditions = conditions;
-
- // If there is no condition, give green pass.
- if (mConditions.isEmpty()) {
- mAllConditionsMet = true;
- return;
- }
-
- // Initializes the conditions map and registers a callback for each condition.
- mConditions.forEach((condition -> mConditionsMap.put(condition, false)));
-
- if (callbacks == null) {
- return;
- }
-
- for (Callback callback : callbacks) {
- addCallback(callback);
- }
}
- @Override
- public void addCallback(@NotNull Callback callback) {
+ private void addConditionLocked(@NotNull Condition condition) {
+ mConditions.add(condition);
+
+ if (!mHaveConditionsStarted) {
+ return;
+ }
+
+ condition.addCallback(mConditionCallback);
+ updateConditionMetState();
+ }
+
+ /**
+ * Adds a condition for the monitor to listen to and consider when determining whether the
+ * overall condition state is met.
+ */
+ public void addCondition(@NotNull Condition condition) {
+ mExecutor.execute(() -> addConditionLocked(condition));
+ }
+
+ /**
+ * Removes a condition from further consideration.
+ */
+ public void removeCondition(@NotNull Condition condition) {
+ mExecutor.execute(() -> {
+ mConditions.remove(condition);
+
+ if (!mHaveConditionsStarted) {
+ return;
+ }
+
+ condition.removeCallback(mConditionCallback);
+ updateConditionMetState();
+ });
+ }
+
+ private void addCallbackLocked(@NotNull Callback callback) {
if (shouldLog()) Log.d(mTag, "adding callback");
mCallbacks.add(callback);
@@ -109,36 +154,36 @@
if (!mHaveConditionsStarted) {
if (shouldLog()) Log.d(mTag, "starting all conditions");
mConditions.forEach(condition -> condition.addCallback(mConditionCallback));
+ updateConditionMetState();
mHaveConditionsStarted = true;
}
}
@Override
- public void removeCallback(@NotNull Callback callback) {
- if (shouldLog()) Log.d(mTag, "removing callback");
- final Iterator<Callback> iterator = mCallbacks.iterator();
- while (iterator.hasNext()) {
- final Callback cb = iterator.next();
- if (cb == null || cb == callback) {
- iterator.remove();
- }
- }
-
- if (mCallbacks.isEmpty() && mHaveConditionsStarted) {
- if (shouldLog()) Log.d(mTag, "stopping all conditions");
- mConditions.forEach(condition -> condition.removeCallback(mConditionCallback));
-
- mAllConditionsMet = false;
- mHaveConditionsStarted = false;
- }
+ public void addCallback(@NotNull Callback callback) {
+ mExecutor.execute(() -> addCallbackLocked(callback));
}
- /**
- * Force updates each condition to the value provided.
- */
- @VisibleForTesting
- public void overrideAllConditionsMet(boolean value) {
- mConditions.forEach(condition -> condition.updateCondition(value));
+ @Override
+ public void removeCallback(@NotNull Callback callback) {
+ mExecutor.execute(() -> {
+ if (shouldLog()) Log.d(mTag, "removing callback");
+ final Iterator<Callback> iterator = mCallbacks.iterator();
+ while (iterator.hasNext()) {
+ final Callback cb = iterator.next();
+ if (cb == null || cb == callback) {
+ iterator.remove();
+ }
+ }
+
+ if (mCallbacks.isEmpty() && mHaveConditionsStarted) {
+ if (shouldLog()) Log.d(mTag, "stopping all conditions");
+ mConditions.forEach(condition -> condition.removeCallback(mConditionCallback));
+
+ mAllConditionsMet = false;
+ mHaveConditionsStarted = false;
+ }
+ });
}
private boolean shouldLog() {
diff --git a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
index b64d7be..d8de07d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
@@ -21,6 +21,7 @@
import com.android.internal.view.RotationPolicy
import com.android.internal.view.RotationPolicy.RotationPolicyListener
import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.traceSection
import javax.inject.Inject
/**
@@ -44,7 +45,9 @@
RotationPolicyWrapper {
override fun setRotationLock(enabled: Boolean) {
- RotationPolicy.setRotationLock(context, enabled)
+ traceSection("RotationPolicyWrapperImpl#setRotationLock") {
+ RotationPolicy.setRotationLock(context, enabled)
+ }
}
override fun setRotationLockAtAngle(enabled: Boolean, rotation: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index c083c14..955d616 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -22,6 +22,7 @@
import android.content.res.Configuration;
import android.media.VolumePolicy;
import android.os.Bundle;
+import android.provider.Settings;
import android.view.WindowManager.LayoutParams;
import com.android.settingslib.applications.InterestingConfigChanges;
@@ -59,6 +60,11 @@
public static final boolean DEFAULT_VOLUME_UP_TO_EXIT_SILENT = false;
public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = false;
+ private static final Intent ZEN_SETTINGS =
+ new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
+ private static final Intent ZEN_PRIORITY_SETTINGS =
+ new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS);
+
protected final Context mContext;
private final VolumeDialogControllerImpl mController;
private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
@@ -191,12 +197,12 @@
private final VolumeDialogImpl.Callback mVolumeDialogCallback = new VolumeDialogImpl.Callback() {
@Override
public void onZenSettingsClicked() {
- startSettings(ZenModePanel.ZEN_SETTINGS);
+ startSettings(ZEN_SETTINGS);
}
@Override
public void onZenPrioritySettingsClicked() {
- startSettings(ZenModePanel.ZEN_PRIORITY_SETTINGS);
+ startSettings(ZEN_PRIORITY_SETTINGS);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 7bb987c..e69de29 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -1,1077 +0,0 @@
-/**
- * Copyright (C) 2014 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.volume;
-
-import android.animation.LayoutTransition;
-import android.animation.LayoutTransition.TransitionListener;
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.content.res.Configuration;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.Settings;
-import android.provider.Settings.Global;
-import android.service.notification.Condition;
-import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.ZenRule;
-import android.text.TextUtils;
-import android.text.format.DateFormat;
-import android.text.format.DateUtils;
-import android.util.ArraySet;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.MathUtils;
-import android.util.Slog;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.RadioButton;
-import android.widget.RadioGroup;
-import android.widget.TextView;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Prefs;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-import java.util.Locale;
-import java.util.Objects;
-
-public class ZenModePanel extends FrameLayout {
- private static final String TAG = "ZenModePanel";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
- public static final int STATE_MODIFY = 0;
- public static final int STATE_AUTO_RULE = 1;
- public static final int STATE_OFF = 2;
-
- private static final int SECONDS_MS = 1000;
- private static final int MINUTES_MS = 60 * SECONDS_MS;
-
- private static final int[] MINUTE_BUCKETS = ZenModeConfig.MINUTE_BUCKETS;
- private static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0];
- private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1];
- private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60);
- private static final int FOREVER_CONDITION_INDEX = 0;
- private static final int COUNTDOWN_CONDITION_INDEX = 1;
- private static final int COUNTDOWN_ALARM_CONDITION_INDEX = 2;
- private static final int COUNTDOWN_CONDITION_COUNT = 2;
-
- public static final Intent ZEN_SETTINGS
- = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
- public static final Intent ZEN_PRIORITY_SETTINGS
- = new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS);
-
- private static final long TRANSITION_DURATION = 300;
-
- private final Context mContext;
- protected final LayoutInflater mInflater;
- private final H mHandler = new H();
- private final ZenPrefs mPrefs;
- private final TransitionHelper mTransitionHelper = new TransitionHelper();
- private final Uri mForeverId;
- private final ConfigurableTexts mConfigurableTexts;
-
- private String mTag = TAG + "/" + Integer.toHexString(System.identityHashCode(this));
-
- protected SegmentedButtons mZenButtons;
- private View mZenIntroduction;
- private TextView mZenIntroductionMessage;
- private View mZenIntroductionConfirm;
- private TextView mZenIntroductionCustomize;
- protected LinearLayout mZenConditions;
- private TextView mZenAlarmWarning;
- private RadioGroup mZenRadioGroup;
- private LinearLayout mZenRadioGroupContent;
-
- private Callback mCallback;
- private ZenModeController mController;
- private Condition mExitCondition;
- private int mBucketIndex = -1;
- private boolean mExpanded;
- private boolean mHidden;
- private int mSessionZen;
- private int mAttachedZen;
- private boolean mAttached;
- private Condition mSessionExitCondition;
- private boolean mVoiceCapable;
-
- protected int mZenModeConditionLayoutId;
- protected int mZenModeButtonLayoutId;
- private View mEmpty;
- private TextView mEmptyText;
- private ImageView mEmptyIcon;
- private View mAutoRule;
- private TextView mAutoTitle;
- private int mState = STATE_MODIFY;
- private ViewGroup mEdit;
-
- public ZenModePanel(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
- mPrefs = new ZenPrefs();
- mInflater = LayoutInflater.from(mContext);
- mForeverId = Condition.newId(mContext).appendPath("forever").build();
- mConfigurableTexts = new ConfigurableTexts(mContext);
- mVoiceCapable = Util.isVoiceCapable(mContext);
- mZenModeConditionLayoutId = R.layout.zen_mode_condition;
- mZenModeButtonLayoutId = R.layout.zen_mode_button;
- if (DEBUG) Log.d(mTag, "new ZenModePanel");
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("ZenModePanel state:");
- pw.print(" mAttached="); pw.println(mAttached);
- pw.print(" mHidden="); pw.println(mHidden);
- pw.print(" mExpanded="); pw.println(mExpanded);
- pw.print(" mSessionZen="); pw.println(mSessionZen);
- pw.print(" mAttachedZen="); pw.println(mAttachedZen);
- pw.print(" mConfirmedPriorityIntroduction=");
- pw.println(mPrefs.mConfirmedPriorityIntroduction);
- pw.print(" mConfirmedSilenceIntroduction=");
- pw.println(mPrefs.mConfirmedSilenceIntroduction);
- pw.print(" mVoiceCapable="); pw.println(mVoiceCapable);
- mTransitionHelper.dump(fd, pw, args);
- }
-
- protected void createZenButtons() {
- mZenButtons = findViewById(R.id.zen_buttons);
- mZenButtons.addButton(R.string.interruption_level_none_twoline,
- R.string.interruption_level_none_with_warning,
- Global.ZEN_MODE_NO_INTERRUPTIONS);
- mZenButtons.addButton(R.string.interruption_level_alarms_twoline,
- R.string.interruption_level_alarms,
- Global.ZEN_MODE_ALARMS);
- mZenButtons.addButton(R.string.interruption_level_priority_twoline,
- R.string.interruption_level_priority,
- Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
- mZenButtons.setCallback(mZenButtonsCallback);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- createZenButtons();
- mZenIntroduction = findViewById(R.id.zen_introduction);
- mZenIntroductionMessage = findViewById(R.id.zen_introduction_message);
- mZenIntroductionConfirm = findViewById(R.id.zen_introduction_confirm);
- mZenIntroductionConfirm.setOnClickListener(v -> confirmZenIntroduction());
- mZenIntroductionCustomize = findViewById(R.id.zen_introduction_customize);
- mZenIntroductionCustomize.setOnClickListener(v -> {
- confirmZenIntroduction();
- if (mCallback != null) {
- mCallback.onPrioritySettings();
- }
- });
- mConfigurableTexts.add(mZenIntroductionCustomize, R.string.zen_priority_customize_button);
-
- mZenConditions = findViewById(R.id.zen_conditions);
- mZenAlarmWarning = findViewById(R.id.zen_alarm_warning);
- mZenRadioGroup = findViewById(R.id.zen_radio_buttons);
- mZenRadioGroupContent = findViewById(R.id.zen_radio_buttons_content);
-
- mEdit = findViewById(R.id.edit_container);
-
- mEmpty = findViewById(android.R.id.empty);
- mEmpty.setVisibility(INVISIBLE);
- mEmptyText = mEmpty.findViewById(android.R.id.title);
- mEmptyIcon = mEmpty.findViewById(android.R.id.icon);
-
- mAutoRule = findViewById(R.id.auto_rule);
- mAutoTitle = mAutoRule.findViewById(android.R.id.title);
- mAutoRule.setVisibility(INVISIBLE);
- }
-
- public void setEmptyState(int icon, int text) {
- mEmptyIcon.post(() -> {
- mEmptyIcon.setImageResource(icon);
- mEmptyText.setText(text);
- });
- }
-
- public void setAutoText(CharSequence text) {
- mAutoTitle.post(() -> mAutoTitle.setText(text));
- }
-
- public void setState(int state) {
- if (mState == state) return;
- transitionFrom(getView(mState), getView(state));
- mState = state;
- }
-
- private void transitionFrom(View from, View to) {
- from.post(() -> {
- // TODO: Better transitions
- to.setAlpha(0);
- to.setVisibility(VISIBLE);
- to.bringToFront();
- to.animate().cancel();
- to.animate().alpha(1)
- .setDuration(TRANSITION_DURATION)
- .withEndAction(() -> from.setVisibility(INVISIBLE))
- .start();
- });
- }
-
- private View getView(int state) {
- switch (state) {
- case STATE_AUTO_RULE:
- return mAutoRule;
- case STATE_OFF:
- return mEmpty;
- default:
- return mEdit;
- }
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- mConfigurableTexts.update();
- if (mZenButtons != null) {
- mZenButtons.update();
- }
- }
-
- private void confirmZenIntroduction() {
- final String prefKey = prefKeyForConfirmation(getSelectedZen(Global.ZEN_MODE_OFF));
- if (prefKey == null) return;
- if (DEBUG) Log.d(TAG, "confirmZenIntroduction " + prefKey);
- Prefs.putBoolean(mContext, prefKey, true);
- mHandler.sendEmptyMessage(H.UPDATE_WIDGETS);
- }
-
- private static String prefKeyForConfirmation(int zen) {
- switch (zen) {
- case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
- return Prefs.Key.DND_CONFIRMED_PRIORITY_INTRODUCTION;
- case Global.ZEN_MODE_NO_INTERRUPTIONS:
- return Prefs.Key.DND_CONFIRMED_SILENCE_INTRODUCTION;
- case Global.ZEN_MODE_ALARMS:
- return Prefs.Key.DND_CONFIRMED_ALARM_INTRODUCTION;
- default:
- return null;
- }
- }
-
- private void onAttach() {
- setExpanded(true);
- mAttachedZen = mController.getZen();
- ZenRule manualRule = mController.getManualRule();
- mExitCondition = manualRule != null ? manualRule.condition : null;
- if (DEBUG) Log.d(mTag, "onAttach " + mAttachedZen + " " + manualRule);
- handleUpdateManualRule(manualRule);
- mZenButtons.setSelectedValue(mAttachedZen, false);
- mSessionZen = mAttachedZen;
- mTransitionHelper.clear();
- mController.addCallback(mZenCallback);
- setSessionExitCondition(copy(mExitCondition));
- updateWidgets();
- setAttached(true);
- }
-
- private void onDetach() {
- if (DEBUG) Log.d(mTag, "onDetach");
- setExpanded(false);
- checkForAttachedZenChange();
- setAttached(false);
- mAttachedZen = -1;
- mSessionZen = -1;
- mController.removeCallback(mZenCallback);
- setSessionExitCondition(null);
- mTransitionHelper.clear();
- }
-
- @VisibleForTesting
- void setAttached(boolean attached) {
- mAttached = attached;
- }
-
- @Override
- public void onVisibilityAggregated(boolean isVisible) {
- super.onVisibilityAggregated(isVisible);
- if (isVisible == mAttached) return;
- if (isVisible) {
- onAttach();
- } else {
- onDetach();
- }
- }
-
- private void setSessionExitCondition(Condition condition) {
- if (Objects.equals(condition, mSessionExitCondition)) return;
- if (DEBUG) Log.d(mTag, "mSessionExitCondition=" + getConditionId(condition));
- mSessionExitCondition = condition;
- }
-
- public void setHidden(boolean hidden) {
- if (mHidden == hidden) return;
- if (DEBUG) Log.d(mTag, "hidden=" + hidden);
- mHidden = hidden;
- updateWidgets();
- }
-
- private void checkForAttachedZenChange() {
- final int selectedZen = getSelectedZen(-1);
- if (DEBUG) Log.d(mTag, "selectedZen=" + selectedZen);
- if (selectedZen != mAttachedZen) {
- if (DEBUG) Log.d(mTag, "attachedZen: " + mAttachedZen + " -> " + selectedZen);
- if (selectedZen == Global.ZEN_MODE_NO_INTERRUPTIONS) {
- mPrefs.trackNoneSelected();
- }
- }
- }
-
- private void setExpanded(boolean expanded) {
- if (expanded == mExpanded) return;
- if (DEBUG) Log.d(mTag, "setExpanded " + expanded);
- mExpanded = expanded;
- updateWidgets();
- fireExpanded();
- }
-
- protected void addZenConditions(int count) {
- for (int i = 0; i < count; i++) {
- final View rb = mInflater.inflate(mZenModeButtonLayoutId, mEdit, false);
- rb.setId(i);
- mZenRadioGroup.addView(rb);
- final View rbc = mInflater.inflate(mZenModeConditionLayoutId, mEdit, false);
- rbc.setId(i + count);
- mZenRadioGroupContent.addView(rbc);
- }
- }
-
- public void init(ZenModeController controller) {
- mController = controller;
- final int minConditions = 1 /*forever*/ + COUNTDOWN_CONDITION_COUNT;
- addZenConditions(minConditions);
- mSessionZen = getSelectedZen(-1);
- handleUpdateManualRule(mController.getManualRule());
- if (DEBUG) Log.d(mTag, "init mExitCondition=" + mExitCondition);
- hideAllConditions();
- }
-
- private void setExitCondition(Condition exitCondition) {
- if (Objects.equals(mExitCondition, exitCondition)) return;
- mExitCondition = exitCondition;
- if (DEBUG) Log.d(mTag, "mExitCondition=" + getConditionId(mExitCondition));
- updateWidgets();
- }
-
- private static Uri getConditionId(Condition condition) {
- return condition != null ? condition.id : null;
- }
-
- private Uri getRealConditionId(Condition condition) {
- return isForever(condition) ? null : getConditionId(condition);
- }
-
- private static Condition copy(Condition condition) {
- return condition == null ? null : condition.copy();
- }
-
- public void setCallback(Callback callback) {
- mCallback = callback;
- }
-
- @VisibleForTesting
- void handleUpdateManualRule(ZenRule rule) {
- final int zen = rule != null ? rule.zenMode : Global.ZEN_MODE_OFF;
- handleUpdateZen(zen);
- final Condition c = rule == null ? null
- : rule.condition != null ? rule.condition
- : createCondition(rule.conditionId);
- handleUpdateConditions(c);
- setExitCondition(c);
- }
-
- private Condition createCondition(Uri conditionId) {
- if (ZenModeConfig.isValidCountdownToAlarmConditionId(conditionId)) {
- long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
- Condition c = ZenModeConfig.toNextAlarmCondition(
- mContext, time, ActivityManager.getCurrentUser());
- return c;
- } else if (ZenModeConfig.isValidCountdownConditionId(conditionId)) {
- long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
- int mins = (int) ((time - System.currentTimeMillis() + DateUtils.MINUTE_IN_MILLIS / 2)
- / DateUtils.MINUTE_IN_MILLIS);
- Condition c = ZenModeConfig.toTimeCondition(mContext, time, mins,
- ActivityManager.getCurrentUser(), false);
- return c;
- }
- // If there is a manual rule, but it has no condition listed then it is forever.
- return forever();
- }
-
- private void handleUpdateZen(int zen) {
- if (mSessionZen != -1 && mSessionZen != zen) {
- mSessionZen = zen;
- }
- mZenButtons.setSelectedValue(zen, false /* fromClick */);
- updateWidgets();
- }
-
- @VisibleForTesting
- int getSelectedZen(int defValue) {
- final Object zen = mZenButtons.getSelectedValue();
- return zen != null ? (Integer) zen : defValue;
- }
-
- private void updateWidgets() {
- if (mTransitionHelper.isTransitioning()) {
- mTransitionHelper.pendingUpdateWidgets();
- return;
- }
- final int zen = getSelectedZen(Global.ZEN_MODE_OFF);
- final boolean zenImportant = zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
- final boolean zenAlarm = zen == Global.ZEN_MODE_ALARMS;
- final boolean introduction = (zenImportant && !mPrefs.mConfirmedPriorityIntroduction
- || zenNone && !mPrefs.mConfirmedSilenceIntroduction
- || zenAlarm && !mPrefs.mConfirmedAlarmIntroduction);
-
- mZenButtons.setVisibility(mHidden ? GONE : VISIBLE);
- mZenIntroduction.setVisibility(introduction ? VISIBLE : GONE);
- if (introduction) {
- int message = zenImportant
- ? R.string.zen_priority_introduction
- : zenAlarm
- ? R.string.zen_alarms_introduction
- : mVoiceCapable
- ? R.string.zen_silence_introduction_voice
- : R.string.zen_silence_introduction;
- mConfigurableTexts.add(mZenIntroductionMessage, message);
- mConfigurableTexts.update();
- mZenIntroductionCustomize.setVisibility(zenImportant ? VISIBLE : GONE);
- }
- final String warning = computeAlarmWarningText(zenNone);
- mZenAlarmWarning.setVisibility(warning != null ? VISIBLE : GONE);
- mZenAlarmWarning.setText(warning);
- }
-
- private String computeAlarmWarningText(boolean zenNone) {
- if (!zenNone) {
- return null;
- }
- final long now = System.currentTimeMillis();
- final long nextAlarm = mController.getNextAlarm();
- if (nextAlarm < now) {
- return null;
- }
- int warningRes = 0;
- if (mSessionExitCondition == null || isForever(mSessionExitCondition)) {
- warningRes = R.string.zen_alarm_warning_indef;
- } else {
- final long time = ZenModeConfig.tryParseCountdownConditionId(mSessionExitCondition.id);
- if (time > now && nextAlarm < time) {
- warningRes = R.string.zen_alarm_warning;
- }
- }
- if (warningRes == 0) {
- return null;
- }
- final boolean soon = (nextAlarm - now) < 24 * 60 * 60 * 1000;
- final boolean is24 = DateFormat.is24HourFormat(mContext, ActivityManager.getCurrentUser());
- final String skeleton = soon ? (is24 ? "Hm" : "hma") : (is24 ? "EEEHm" : "EEEhma");
- final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
- final CharSequence formattedTime = DateFormat.format(pattern, nextAlarm);
- final int templateRes = soon ? R.string.alarm_template : R.string.alarm_template_far;
- final String template = getResources().getString(templateRes, formattedTime);
- return getResources().getString(warningRes, template);
- }
-
- @VisibleForTesting
- void handleUpdateConditions(Condition c) {
- if (mTransitionHelper.isTransitioning()) {
- return;
- }
- // forever
- bind(forever(), mZenRadioGroupContent.getChildAt(FOREVER_CONDITION_INDEX),
- FOREVER_CONDITION_INDEX);
- if (c == null) {
- bindGenericCountdown();
- bindNextAlarm(getTimeUntilNextAlarmCondition());
- } else if (isForever(c)) {
-
- getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true);
- bindGenericCountdown();
- bindNextAlarm(getTimeUntilNextAlarmCondition());
- } else {
- if (isAlarm(c)) {
- bindGenericCountdown();
- bindNextAlarm(c);
- getConditionTagAt(COUNTDOWN_ALARM_CONDITION_INDEX).rb.setChecked(true);
- } else if (isCountdown(c)) {
- bindNextAlarm(getTimeUntilNextAlarmCondition());
- bind(c, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
- COUNTDOWN_CONDITION_INDEX);
- getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
- } else {
- Slog.wtf(TAG, "Invalid manual condition: " + c);
- }
- }
- mZenConditions.setVisibility(mSessionZen != Global.ZEN_MODE_OFF ? View.VISIBLE : View.GONE);
- }
-
- private void bindGenericCountdown() {
- mBucketIndex = DEFAULT_BUCKET_INDEX;
- Condition countdown = ZenModeConfig.toTimeCondition(mContext,
- MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
- // don't change the hour condition while the user is viewing the panel
- if (!mAttached || getConditionTagAt(COUNTDOWN_CONDITION_INDEX).condition == null) {
- bind(countdown, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
- COUNTDOWN_CONDITION_INDEX);
- }
- }
-
- private void bindNextAlarm(Condition c) {
- View alarmContent = mZenRadioGroupContent.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX);
- ConditionTag tag = (ConditionTag) alarmContent.getTag();
- // Don't change the alarm condition while the user is viewing the panel
- if (c != null && (!mAttached || tag == null || tag.condition == null)) {
- bind(c, alarmContent, COUNTDOWN_ALARM_CONDITION_INDEX);
- }
-
- tag = (ConditionTag) alarmContent.getTag();
- boolean showAlarm = tag != null && tag.condition != null;
- mZenRadioGroup.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX).setVisibility(
- showAlarm ? View.VISIBLE : View.INVISIBLE);
- alarmContent.setVisibility(showAlarm ? View.VISIBLE : View.INVISIBLE);
- }
-
- private Condition forever() {
- return new Condition(mForeverId, foreverSummary(mContext), "", "", 0 /*icon*/,
- Condition.STATE_TRUE, 0 /*flags*/);
- }
-
- private static String foreverSummary(Context context) {
- return context.getString(com.android.internal.R.string.zen_mode_forever);
- }
-
- // Returns a time condition if the next alarm is within the next week.
- private Condition getTimeUntilNextAlarmCondition() {
- GregorianCalendar weekRange = new GregorianCalendar();
- setToMidnight(weekRange);
- weekRange.add(Calendar.DATE, 6);
- final long nextAlarmMs = mController.getNextAlarm();
- if (nextAlarmMs > 0) {
- GregorianCalendar nextAlarm = new GregorianCalendar();
- nextAlarm.setTimeInMillis(nextAlarmMs);
- setToMidnight(nextAlarm);
-
- if (weekRange.compareTo(nextAlarm) >= 0) {
- return ZenModeConfig.toNextAlarmCondition(mContext, nextAlarmMs,
- ActivityManager.getCurrentUser());
- }
- }
- return null;
- }
-
- private void setToMidnight(Calendar calendar) {
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- calendar.set(Calendar.MILLISECOND, 0);
- }
-
- @VisibleForTesting
- ConditionTag getConditionTagAt(int index) {
- return (ConditionTag) mZenRadioGroupContent.getChildAt(index).getTag();
- }
-
- @VisibleForTesting
- int getVisibleConditions() {
- int rt = 0;
- final int N = mZenRadioGroupContent.getChildCount();
- for (int i = 0; i < N; i++) {
- rt += mZenRadioGroupContent.getChildAt(i).getVisibility() == VISIBLE ? 1 : 0;
- }
- return rt;
- }
-
- private void hideAllConditions() {
- final int N = mZenRadioGroupContent.getChildCount();
- for (int i = 0; i < N; i++) {
- mZenRadioGroupContent.getChildAt(i).setVisibility(GONE);
- }
- }
-
- private static boolean isAlarm(Condition c) {
- return c != null && ZenModeConfig.isValidCountdownToAlarmConditionId(c.id);
- }
-
- private static boolean isCountdown(Condition c) {
- return c != null && ZenModeConfig.isValidCountdownConditionId(c.id);
- }
-
- private boolean isForever(Condition c) {
- return c != null && mForeverId.equals(c.id);
- }
-
- private void bind(final Condition condition, final View row, final int rowId) {
- if (condition == null) throw new IllegalArgumentException("condition must not be null");
- final boolean enabled = condition.state == Condition.STATE_TRUE;
- final ConditionTag tag =
- row.getTag() != null ? (ConditionTag) row.getTag() : new ConditionTag();
- row.setTag(tag);
- final boolean first = tag.rb == null;
- if (tag.rb == null) {
- tag.rb = (RadioButton) mZenRadioGroup.getChildAt(rowId);
- }
- tag.condition = condition;
- final Uri conditionId = getConditionId(tag.condition);
- if (DEBUG) Log.d(mTag, "bind i=" + mZenRadioGroupContent.indexOfChild(row) + " first="
- + first + " condition=" + conditionId);
- tag.rb.setEnabled(enabled);
- tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (mExpanded && isChecked) {
- tag.rb.setChecked(true);
- if (DEBUG) Log.d(mTag, "onCheckedChanged " + conditionId);
- MetricsLogger.action(mContext, MetricsEvent.QS_DND_CONDITION_SELECT);
- select(tag.condition);
- announceConditionSelection(tag);
- }
- }
- });
-
- if (tag.lines == null) {
- tag.lines = row.findViewById(android.R.id.content);
- }
- if (tag.line1 == null) {
- tag.line1 = (TextView) row.findViewById(android.R.id.text1);
- mConfigurableTexts.add(tag.line1);
- }
- if (tag.line2 == null) {
- tag.line2 = (TextView) row.findViewById(android.R.id.text2);
- mConfigurableTexts.add(tag.line2);
- }
- final String line1 = !TextUtils.isEmpty(condition.line1) ? condition.line1
- : condition.summary;
- final String line2 = condition.line2;
- tag.line1.setText(line1);
- if (TextUtils.isEmpty(line2)) {
- tag.line2.setVisibility(GONE);
- } else {
- tag.line2.setVisibility(VISIBLE);
- tag.line2.setText(line2);
- }
- tag.lines.setEnabled(enabled);
- tag.lines.setAlpha(enabled ? 1 : .4f);
-
- final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1);
- button1.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- onClickTimeButton(row, tag, false /*down*/, rowId);
- }
- });
-
- final ImageView button2 = (ImageView) row.findViewById(android.R.id.button2);
- button2.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- onClickTimeButton(row, tag, true /*up*/, rowId);
- }
- });
- tag.lines.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- tag.rb.setChecked(true);
- }
- });
-
- final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
- if (rowId != COUNTDOWN_ALARM_CONDITION_INDEX && time > 0) {
- button1.setVisibility(VISIBLE);
- button2.setVisibility(VISIBLE);
- if (mBucketIndex > -1) {
- button1.setEnabled(mBucketIndex > 0);
- button2.setEnabled(mBucketIndex < MINUTE_BUCKETS.length - 1);
- } else {
- final long span = time - System.currentTimeMillis();
- button1.setEnabled(span > MIN_BUCKET_MINUTES * MINUTES_MS);
- final Condition maxCondition = ZenModeConfig.toTimeCondition(mContext,
- MAX_BUCKET_MINUTES, ActivityManager.getCurrentUser());
- button2.setEnabled(!Objects.equals(condition.summary, maxCondition.summary));
- }
-
- button1.setAlpha(button1.isEnabled() ? 1f : .5f);
- button2.setAlpha(button2.isEnabled() ? 1f : .5f);
- } else {
- button1.setVisibility(GONE);
- button2.setVisibility(GONE);
- }
- // wire up interaction callbacks for newly-added condition rows
- if (first) {
- Interaction.register(tag.rb, mInteractionCallback);
- Interaction.register(tag.lines, mInteractionCallback);
- Interaction.register(button1, mInteractionCallback);
- Interaction.register(button2, mInteractionCallback);
- }
- row.setVisibility(VISIBLE);
- }
-
- private void announceConditionSelection(ConditionTag tag) {
- final int zen = getSelectedZen(Global.ZEN_MODE_OFF);
- String modeText;
- switch(zen) {
- case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
- modeText = mContext.getString(R.string.interruption_level_priority);
- break;
- case Global.ZEN_MODE_NO_INTERRUPTIONS:
- modeText = mContext.getString(R.string.interruption_level_none);
- break;
- case Global.ZEN_MODE_ALARMS:
- modeText = mContext.getString(R.string.interruption_level_alarms);
- break;
- default:
- return;
- }
- announceForAccessibility(mContext.getString(R.string.zen_mode_and_condition, modeText,
- tag.line1.getText()));
- }
-
- private void onClickTimeButton(View row, ConditionTag tag, boolean up, int rowId) {
- MetricsLogger.action(mContext, MetricsEvent.QS_DND_TIME, up);
- Condition newCondition = null;
- final int N = MINUTE_BUCKETS.length;
- if (mBucketIndex == -1) {
- // not on a known index, search for the next or prev bucket by time
- final Uri conditionId = getConditionId(tag.condition);
- final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
- final long now = System.currentTimeMillis();
- for (int i = 0; i < N; i++) {
- int j = up ? i : N - 1 - i;
- final int bucketMinutes = MINUTE_BUCKETS[j];
- final long bucketTime = now + bucketMinutes * MINUTES_MS;
- if (up && bucketTime > time || !up && bucketTime < time) {
- mBucketIndex = j;
- newCondition = ZenModeConfig.toTimeCondition(mContext,
- bucketTime, bucketMinutes, ActivityManager.getCurrentUser(),
- false /*shortVersion*/);
- break;
- }
- }
- if (newCondition == null) {
- mBucketIndex = DEFAULT_BUCKET_INDEX;
- newCondition = ZenModeConfig.toTimeCondition(mContext,
- MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
- }
- } else {
- // on a known index, simply increment or decrement
- mBucketIndex = Math.max(0, Math.min(N - 1, mBucketIndex + (up ? 1 : -1)));
- newCondition = ZenModeConfig.toTimeCondition(mContext,
- MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
- }
- bind(newCondition, row, rowId);
- tag.rb.setChecked(true);
- select(newCondition);
- announceConditionSelection(tag);
- }
-
- private void select(final Condition condition) {
- if (DEBUG) Log.d(mTag, "select " + condition);
- if (mSessionZen == -1 || mSessionZen == Global.ZEN_MODE_OFF) {
- if (DEBUG) Log.d(mTag, "Ignoring condition selection outside of manual zen");
- return;
- }
- final Uri realConditionId = getRealConditionId(condition);
- if (mController != null) {
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- mController.setZen(mSessionZen, realConditionId, TAG + ".selectCondition");
- }
- });
- }
- setExitCondition(condition);
- if (realConditionId == null) {
- mPrefs.setMinuteIndex(-1);
- } else if ((isAlarm(condition) || isCountdown(condition)) && mBucketIndex != -1) {
- mPrefs.setMinuteIndex(mBucketIndex);
- }
- setSessionExitCondition(copy(condition));
- }
-
- private void fireInteraction() {
- if (mCallback != null) {
- mCallback.onInteraction();
- }
- }
-
- private void fireExpanded() {
- if (mCallback != null) {
- mCallback.onExpanded(mExpanded);
- }
- }
-
- private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
- @Override
- public void onManualRuleChanged(ZenRule rule) {
- mHandler.obtainMessage(H.MANUAL_RULE_CHANGED, rule).sendToTarget();
- }
- };
-
- private final class H extends Handler {
- private static final int MANUAL_RULE_CHANGED = 2;
- private static final int UPDATE_WIDGETS = 3;
-
- private H() {
- super(Looper.getMainLooper());
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MANUAL_RULE_CHANGED: handleUpdateManualRule((ZenRule) msg.obj); break;
- case UPDATE_WIDGETS: updateWidgets(); break;
- }
- }
- }
-
- public interface Callback {
- void onPrioritySettings();
- void onInteraction();
- void onExpanded(boolean expanded);
- }
-
- // used as the view tag on condition rows
- @VisibleForTesting
- static class ConditionTag {
- RadioButton rb;
- View lines;
- TextView line1;
- TextView line2;
- Condition condition;
- }
-
- private final class ZenPrefs implements OnSharedPreferenceChangeListener {
- private final int mNoneDangerousThreshold;
-
- private int mMinuteIndex;
- private int mNoneSelected;
- private boolean mConfirmedPriorityIntroduction;
- private boolean mConfirmedSilenceIntroduction;
- private boolean mConfirmedAlarmIntroduction;
-
- private ZenPrefs() {
- mNoneDangerousThreshold = mContext.getResources()
- .getInteger(R.integer.zen_mode_alarm_warning_threshold);
- Prefs.registerListener(mContext, this);
- updateMinuteIndex();
- updateNoneSelected();
- updateConfirmedPriorityIntroduction();
- updateConfirmedSilenceIntroduction();
- updateConfirmedAlarmIntroduction();
- }
-
- public void trackNoneSelected() {
- mNoneSelected = clampNoneSelected(mNoneSelected + 1);
- if (DEBUG) Log.d(mTag, "Setting none selected: " + mNoneSelected + " threshold="
- + mNoneDangerousThreshold);
- Prefs.putInt(mContext, Prefs.Key.DND_NONE_SELECTED, mNoneSelected);
- }
-
- public int getMinuteIndex() {
- return mMinuteIndex;
- }
-
- public void setMinuteIndex(int minuteIndex) {
- minuteIndex = clampIndex(minuteIndex);
- if (minuteIndex == mMinuteIndex) return;
- mMinuteIndex = clampIndex(minuteIndex);
- if (DEBUG) Log.d(mTag, "Setting favorite minute index: " + mMinuteIndex);
- Prefs.putInt(mContext, Prefs.Key.DND_FAVORITE_BUCKET_INDEX, mMinuteIndex);
- }
-
- @Override
- public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
- updateMinuteIndex();
- updateNoneSelected();
- updateConfirmedPriorityIntroduction();
- updateConfirmedSilenceIntroduction();
- updateConfirmedAlarmIntroduction();
- }
-
- private void updateMinuteIndex() {
- mMinuteIndex = clampIndex(Prefs.getInt(mContext,
- Prefs.Key.DND_FAVORITE_BUCKET_INDEX, DEFAULT_BUCKET_INDEX));
- if (DEBUG) Log.d(mTag, "Favorite minute index: " + mMinuteIndex);
- }
-
- private int clampIndex(int index) {
- return MathUtils.constrain(index, -1, MINUTE_BUCKETS.length - 1);
- }
-
- private void updateNoneSelected() {
- mNoneSelected = clampNoneSelected(Prefs.getInt(mContext,
- Prefs.Key.DND_NONE_SELECTED, 0));
- if (DEBUG) Log.d(mTag, "None selected: " + mNoneSelected);
- }
-
- private int clampNoneSelected(int noneSelected) {
- return MathUtils.constrain(noneSelected, 0, Integer.MAX_VALUE);
- }
-
- private void updateConfirmedPriorityIntroduction() {
- final boolean confirmed = Prefs.getBoolean(mContext,
- Prefs.Key.DND_CONFIRMED_PRIORITY_INTRODUCTION, false);
- if (confirmed == mConfirmedPriorityIntroduction) return;
- mConfirmedPriorityIntroduction = confirmed;
- if (DEBUG) Log.d(mTag, "Confirmed priority introduction: "
- + mConfirmedPriorityIntroduction);
- }
-
- private void updateConfirmedSilenceIntroduction() {
- final boolean confirmed = Prefs.getBoolean(mContext,
- Prefs.Key.DND_CONFIRMED_SILENCE_INTRODUCTION, false);
- if (confirmed == mConfirmedSilenceIntroduction) return;
- mConfirmedSilenceIntroduction = confirmed;
- if (DEBUG) Log.d(mTag, "Confirmed silence introduction: "
- + mConfirmedSilenceIntroduction);
- }
-
- private void updateConfirmedAlarmIntroduction() {
- final boolean confirmed = Prefs.getBoolean(mContext,
- Prefs.Key.DND_CONFIRMED_ALARM_INTRODUCTION, false);
- if (confirmed == mConfirmedAlarmIntroduction) return;
- mConfirmedAlarmIntroduction = confirmed;
- if (DEBUG) Log.d(mTag, "Confirmed alarm introduction: "
- + mConfirmedAlarmIntroduction);
- }
- }
-
- protected final SegmentedButtons.Callback mZenButtonsCallback = new SegmentedButtons.Callback() {
- @Override
- public void onSelected(final Object value, boolean fromClick) {
- if (value != null && mZenButtons.isShown() && isAttachedToWindow()) {
- final int zen = (Integer) value;
- if (fromClick) {
- MetricsLogger.action(mContext, MetricsEvent.QS_DND_ZEN_SELECT, zen);
- }
- if (DEBUG) Log.d(mTag, "mZenButtonsCallback selected=" + zen);
- final Uri realConditionId = getRealConditionId(mSessionExitCondition);
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- mController.setZen(zen, realConditionId, TAG + ".selectZen");
- if (zen != Global.ZEN_MODE_OFF) {
- Prefs.putInt(mContext, Prefs.Key.DND_FAVORITE_ZEN, zen);
- }
- }
- });
- }
- }
-
- @Override
- public void onInteraction() {
- fireInteraction();
- }
- };
-
- private final Interaction.Callback mInteractionCallback = new Interaction.Callback() {
- @Override
- public void onInteraction() {
- fireInteraction();
- }
- };
-
- private final class TransitionHelper implements TransitionListener, Runnable {
- private final ArraySet<View> mTransitioningViews = new ArraySet<View>();
-
- private boolean mTransitioning;
- private boolean mPendingUpdateWidgets;
-
- public void clear() {
- mTransitioningViews.clear();
- mPendingUpdateWidgets = false;
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println(" TransitionHelper state:");
- pw.print(" mPendingUpdateWidgets="); pw.println(mPendingUpdateWidgets);
- pw.print(" mTransitioning="); pw.println(mTransitioning);
- pw.print(" mTransitioningViews="); pw.println(mTransitioningViews);
- }
-
- public void pendingUpdateWidgets() {
- mPendingUpdateWidgets = true;
- }
-
- public boolean isTransitioning() {
- return !mTransitioningViews.isEmpty();
- }
-
- @Override
- public void startTransition(LayoutTransition transition,
- ViewGroup container, View view, int transitionType) {
- mTransitioningViews.add(view);
- updateTransitioning();
- }
-
- @Override
- public void endTransition(LayoutTransition transition,
- ViewGroup container, View view, int transitionType) {
- mTransitioningViews.remove(view);
- updateTransitioning();
- }
-
- @Override
- public void run() {
- if (DEBUG) Log.d(mTag, "TransitionHelper run"
- + " mPendingUpdateWidgets=" + mPendingUpdateWidgets);
- if (mPendingUpdateWidgets) {
- updateWidgets();
- }
- mPendingUpdateWidgets = false;
- }
-
- private void updateTransitioning() {
- final boolean transitioning = isTransitioning();
- if (mTransitioning == transitioning) return;
- mTransitioning = transitioning;
- if (DEBUG) Log.d(mTag, "TransitionHelper mTransitioning=" + mTransitioning);
- if (!mTransitioning) {
- if (mPendingUpdateWidgets) {
- mHandler.post(this);
- } else {
- mPendingUpdateWidgets = false;
- }
- }
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
index 9f8f6c1..06082b6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
@@ -32,6 +32,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -66,6 +67,11 @@
mController.setupUri(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
}
+ @After
+ public void tearDown() {
+ mController.onViewDetached();
+ }
+
@Test
public void refresh_replacesSliceContentAndNotifiesListener() {
mController.refresh();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java
index 2d52c42..c5b1a1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java
@@ -16,7 +16,8 @@
package com.android.systemui.communal;
-import static org.mockito.ArgumentMatchers.anyBoolean;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
@@ -59,7 +60,8 @@
final Condition.Callback callback = mock(Condition.Callback.class);
mCondition.addCallback(callback);
- verify(callback).onConditionChanged(mCondition, true);
+ verify(callback).onConditionChanged(mCondition);
+ assertThat(mCondition.isConditionMet()).isTrue();
}
@Test
@@ -68,7 +70,7 @@
final Condition.Callback callback = mock(Condition.Callback.class);
mCondition.addCallback(callback);
- verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+ verify(callback, never()).onConditionChanged(eq(mCondition));
}
@Test
@@ -80,7 +82,8 @@
clearInvocations(callback);
updateCommunalSetting(true);
- verify(callback).onConditionChanged(mCondition, true);
+ verify(callback).onConditionChanged(mCondition);
+ assertThat(mCondition.isConditionMet()).isTrue();
}
@Test
@@ -92,7 +95,8 @@
clearInvocations(callback);
updateCommunalSetting(false);
- verify(callback).onConditionChanged(mCondition, false);
+ verify(callback).onConditionChanged(mCondition);
+ assertThat(mCondition.isConditionMet()).isFalse();
}
@Test
@@ -104,7 +108,8 @@
clearInvocations(callback);
updateCommunalSetting(true);
- verify(callback, never()).onConditionChanged(mCondition, true);
+ verify(callback, never()).onConditionChanged(mCondition);
+ assertThat(mCondition.isConditionMet()).isTrue();
}
private void updateCommunalSetting(boolean value) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java
index 61a5126..500205c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java
@@ -16,7 +16,8 @@
package com.android.systemui.communal;
-import static org.mockito.ArgumentMatchers.anyBoolean;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.clearInvocations;
@@ -49,6 +50,7 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@SmallTest
@@ -89,7 +91,8 @@
networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
// Verifies that the callback is triggered.
- verify(callback).onConditionChanged(mCondition, true);
+ verify(callback).onConditionChanged(mCondition);
+ assertThat(mCondition.isConditionMet()).isTrue();
}
@Test
@@ -110,7 +113,7 @@
networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi2));
// Verifies that the callback is not triggered.
- verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+ verify(callback, never()).onConditionChanged(eq(mCondition));
}
@Test
@@ -126,11 +129,13 @@
networkCallback.onAvailable(network);
networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
+ Mockito.clearInvocations(callback);
// Connected to non-trusted Wi-Fi network.
networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities("random-wifi"));
// Verifies that the callback is triggered.
- verify(callback).onConditionChanged(mCondition, false);
+ verify(callback).onConditionChanged(mCondition);
+ assertThat(mCondition.isConditionMet()).isFalse();
}
@Test
@@ -151,7 +156,8 @@
networkCallback.onLost(network);
// Verifies that the callback is triggered.
- verify(callback).onConditionChanged(mCondition, false);
+ verify(callback).onConditionChanged(mCondition);
+ assertThat(mCondition.isConditionMet()).isFalse();
}
// Captures and returns the network callback, assuming it is registered with the connectivity
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index cdffaec..7e1edd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -35,6 +35,7 @@
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
@@ -306,4 +307,11 @@
// THEN the display screen state will change
assertEquals(Display.STATE_DOZE_SUSPEND, mServiceFake.screenState);
}
+
+ @Test
+ public void authCallbackRemovedOnDestroy() {
+ mScreen.destroy();
+
+ verify(mAuthController).removeCallback(anyObject());
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 609291a..708fc91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -74,6 +74,7 @@
private const val SESSION_ARTIST = "SESSION_ARTIST"
private const val SESSION_TITLE = "SESSION_TITLE"
private const val USER_ID = 0
+private const val DISABLED_DEVICE_NAME = "DISABLED_DEVICE_NAME"
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -131,7 +132,7 @@
private lateinit var session: MediaSession
private val device = MediaDeviceData(true, null, DEVICE_NAME)
- private val disabledDevice = MediaDeviceData(false, null, "Disabled Device")
+ private val disabledDevice = MediaDeviceData(false, null, DISABLED_DEVICE_NAME)
private lateinit var mediaData: MediaData
private val clock = FakeSystemClock()
@@ -396,13 +397,12 @@
@Test
fun bindDisabledDevice() {
seamless.id = 1
- val fallbackString = context.getString(R.string.media_seamless_other_device)
player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
val state = mediaData.copy(device = disabledDevice)
player.bindPlayer(state, PACKAGE)
assertThat(seamless.isEnabled()).isFalse()
- assertThat(seamlessText.getText()).isEqualTo(fallbackString)
- assertThat(seamless.contentDescription).isEqualTo(fallbackString)
+ assertThat(seamlessText.getText()).isEqualTo(DISABLED_DEVICE_NAME)
+ assertThat(seamless.contentDescription).isEqualTo(DISABLED_DEVICE_NAME)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 649ee87..f4fa921 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -1,6 +1,5 @@
package com.android.systemui.media
-import android.app.Notification
import android.app.Notification.MediaStyle
import android.app.PendingIntent
import android.app.smartspace.SmartspaceAction
@@ -240,15 +239,14 @@
@Test
fun testOnNotificationAdded_isRcn_markedRemote() {
- val bundle = Bundle().apply {
- putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, "Remote Cast Notification")
- }
val rcn = SbnBuilder().run {
setPkg("com.android.systemui") // System package
modifyNotification(context).also {
it.setSmallIcon(android.R.drawable.ic_media_pause)
- it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
- it.addExtras(bundle)
+ it.setStyle(MediaStyle().apply {
+ setMediaSession(session.sessionToken)
+ setRemotePlaybackInfo("Remote device", 0, null)
+ })
}
build()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index 64b5b86..d912a89 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -437,6 +437,24 @@
verify(mr2, never()).getRoutingSessionForMediaController(eq(controller))
}
+ @Test
+ fun testRemotePlaybackDeviceOverride() {
+ whenever(route.name).thenReturn(DEVICE_NAME)
+ val deviceData = MediaDeviceData(false, null, REMOTE_DEVICE_NAME, null)
+ val mediaDataWithDevice = mediaData.copy(device = deviceData)
+
+ // GIVEN media data that already has a device set
+ manager.onMediaDataLoaded(KEY, null, mediaDataWithDevice)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ // THEN we keep the device info, and don't register a listener
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isFalse()
+ assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME)
+ verify(lmm, never()).registerCallback(any())
+ }
+
fun captureCallback(): LocalMediaManager.DeviceCallback {
val captor = ArgumentCaptor.forClass(LocalMediaManager.DeviceCallback::class.java)
verify(lmm).registerCallback(captor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 0576987..bdc3117 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -201,7 +201,7 @@
assertThat(devices.containsAll(mMediaDevices)).isTrue();
assertThat(devices.size()).isEqualTo(mMediaDevices.size());
- verify(mCb).onRouteChanged();
+ verify(mCb).onDeviceListChanged();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
deleted file mode 100644
index 84776c7..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2017 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.qs;
-
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_MORE_SETTINGS;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-import android.testing.ViewUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-@SmallTest
-public class QSDetailTest extends SysuiTestCase {
-
- private MetricsLogger mMetricsLogger;
- private QSDetail mQsDetail;
- private QSPanelController mQsPanelController;
- private QuickStatusBarHeader mQuickHeader;
- private ActivityStarter mActivityStarter;
- private DetailAdapter mMockDetailAdapter;
- private TestableLooper mTestableLooper;
- private UiEventLoggerFake mUiEventLogger;
- private FrameLayout mParent;
-
- @Before
- public void setup() throws Exception {
- mTestableLooper = TestableLooper.get(this);
- mUiEventLogger = QSEvents.INSTANCE.setLoggerForTesting();
-
- mParent = new FrameLayout(mContext);
- mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
- mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class);
- LayoutInflater.from(mContext).inflate(R.layout.qs_detail, mParent);
- mQsDetail = (QSDetail) mParent.getChildAt(0);
-
- mQsPanelController = mock(QSPanelController.class);
- mQuickHeader = mock(QuickStatusBarHeader.class);
- mQsDetail.setQsPanel(mQsPanelController, mQuickHeader, mock(QSFooter.class),
- mock(FalsingManager.class));
- mQsDetail.mClipper = mock(QSDetailClipper.class);
-
- mMockDetailAdapter = mock(DetailAdapter.class);
- when(mMockDetailAdapter.createDetailView(any(), any(), any()))
- .thenReturn(new View(mContext));
-
- // Only detail in use is the user detail
- when(mMockDetailAdapter.openDetailEvent())
- .thenReturn(QSUserSwitcherEvent.QS_USER_DETAIL_OPEN);
- when(mMockDetailAdapter.closeDetailEvent())
- .thenReturn(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE);
- when(mMockDetailAdapter.moreSettingsEvent())
- .thenReturn(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS);
- ViewUtils.attachView(mParent);
- }
-
- @After
- public void tearDown() {
- QSEvents.INSTANCE.resetLogger();
- mTestableLooper.processAllMessages();
- ViewUtils.detachView(mParent);
- }
-
- @Test
- public void testShowDetail_Metrics() {
- mTestableLooper.processAllMessages();
-
- mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
- verify(mMetricsLogger).visible(eq(mMockDetailAdapter.getMetricsCategory()));
- assertEquals(1, mUiEventLogger.numLogs());
- assertEquals(QSUserSwitcherEvent.QS_USER_DETAIL_OPEN.getId(), mUiEventLogger.eventId(0));
- mUiEventLogger.getLogs().clear();
-
- mQsDetail.handleShowingDetail(null, 0, 0, false);
- verify(mMetricsLogger).hidden(eq(mMockDetailAdapter.getMetricsCategory()));
-
- assertEquals(1, mUiEventLogger.numLogs());
- assertEquals(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE.getId(), mUiEventLogger.eventId(0));
- }
-
- @Test
- public void testShowDetail_ShouldAnimate() {
- mTestableLooper.processAllMessages();
-
- when(mMockDetailAdapter.shouldAnimate()).thenReturn(true);
- mQsDetail.setFullyExpanded(true);
-
- mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
- verify(mQsDetail.mClipper).updateCircularClip(eq(true) /* animate */, anyInt(), anyInt(),
- eq(true) /* in */, any());
- clearInvocations(mQsDetail.mClipper);
-
- mQsDetail.handleShowingDetail(null, 0, 0, false);
- verify(mQsDetail.mClipper).updateCircularClip(eq(true) /* animate */, anyInt(), anyInt(),
- eq(false) /* in */, any());
- }
-
- @Test
- public void testShowDetail_ShouldNotAnimate() {
- mTestableLooper.processAllMessages();
-
- when(mMockDetailAdapter.shouldAnimate()).thenReturn(false);
- mQsDetail.setFullyExpanded(true);
-
- mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
- verify(mQsDetail.mClipper).updateCircularClip(eq(false) /* animate */, anyInt(), anyInt(),
- eq(true) /* in */, any());
- clearInvocations(mQsDetail.mClipper);
-
- // Detail adapters should always animate on close. shouldAnimate() should only affect the
- // open transition
- mQsDetail.handleShowingDetail(null, 0, 0, false);
- verify(mQsDetail.mClipper).updateCircularClip(eq(true) /* animate */, anyInt(), anyInt(),
- eq(false) /* in */, any());
- }
-
- @Test
- public void testDoneButton_CloseDetailPanel() {
- mTestableLooper.processAllMessages();
-
- when(mMockDetailAdapter.onDoneButtonClicked()).thenReturn(false);
-
- mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
- mQsDetail.requireViewById(android.R.id.button1).performClick();
- verify(mQsPanelController).closeDetail();
- }
-
- @Test
- public void testDoneButton_KeepDetailPanelOpen() {
- mTestableLooper.processAllMessages();
-
- when(mMockDetailAdapter.onDoneButtonClicked()).thenReturn(true);
-
- mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
- mQsDetail.requireViewById(android.R.id.button1).performClick();
- verify(mQsPanelController, never()).closeDetail();
- }
-
- @Test
- public void testMoreSettingsButton() {
- mTestableLooper.processAllMessages();
-
- mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
- mUiEventLogger.getLogs().clear();
- mQsDetail.requireViewById(android.R.id.button2).performClick();
-
- int metricsCategory = mMockDetailAdapter.getMetricsCategory();
- verify(mMetricsLogger).action(eq(ACTION_QS_MORE_SETTINGS), eq(metricsCategory));
- assertEquals(1, mUiEventLogger.numLogs());
- assertEquals(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS.getId(), mUiEventLogger.eventId(0));
-
- verify(mActivityStarter).postStartActivityDismissingKeyguard(any(), anyInt());
- }
-
- @Test
- public void testNullAdapterClick() {
- DetailAdapter mock = mock(DetailAdapter.class);
- when(mock.moreSettingsEvent()).thenReturn(DetailAdapter.INVALID);
- mQsDetail.setupDetailFooter(mock);
- mQsDetail.requireViewById(android.R.id.button2).performClick();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 3266d6a..4ab3926 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -146,7 +146,6 @@
mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class),
mock(SecureSettings.class), mock(CustomTileStatePersister.class),
mTileServiceRequestControllerBuilder, mock(TileLifecycleManager.Factory.class));
- qs.setHost(host);
qs.setListening(true);
processAllMessages();
@@ -186,7 +185,6 @@
mock(QSTileHost.class),
mock(StatusBarStateController.class),
commandQueue,
- new QSDetailDisplayer(),
mQSMediaHost,
mQQSMediaHost,
mBypassController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
deleted file mode 100644
index b2ca62f..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2020 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.qs;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-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.content.res.Configuration;
-import android.content.res.Resources;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.classifier.FalsingManagerFake;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.media.MediaHost;
-import com.android.systemui.plugins.qs.QSTileView;
-import com.android.systemui.qs.customize.QSCustomizerController;
-import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.settings.brightness.BrightnessController;
-import com.android.systemui.settings.brightness.BrightnessSliderController;
-import com.android.systemui.settings.brightness.ToggleSlider;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.animation.DisappearParameters;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Collections;
-
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-@SmallTest
-public class QSPanelControllerTest extends SysuiTestCase {
-
- @Mock
- private QSPanel mQSPanel;
- @Mock
- private QSTileHost mQSTileHost;
- @Mock
- private QSCustomizerController mQSCustomizerController;
- @Mock
- private QSTileRevealController.Factory mQSTileRevealControllerFactory;
- @Mock
- private QSTileRevealController mQSTileRevealController;
- @Mock
- private MediaHost mMediaHost;
- @Mock
- private MetricsLogger mMetricsLogger;
- private UiEventLogger mUiEventLogger = new UiEventLoggerFake();
- private DumpManager mDumpManager = new DumpManager();
- @Mock
- private TunerService mTunerService;
- @Mock
- private QSFgsManagerFooter mQSFgsManagerFooter;
- @Mock
- private QSSecurityFooter mQSSecurityFooter;
- @Mock
- private QSLogger mQSLogger;
- @Mock
- private BrightnessController.Factory mBrightnessControllerFactory;
- @Mock
- private BrightnessController mBrightnessController;
- @Mock
- private BrightnessSliderController.Factory mToggleSliderViewControllerFactory;
- @Mock
- private BrightnessSliderController mBrightnessSliderController;
- @Mock
- QSTileImpl mQSTile;
- @Mock
- QSTileView mQSTileView;
- @Mock
- PagedTileLayout mPagedTileLayout;
- @Mock
- CommandQueue mCommandQueue;
- FalsingManagerFake mFalsingManager = new FalsingManagerFake();
- @Mock
- Resources mResources;
- @Mock
- Configuration mConfiguration;
- @Mock
- FeatureFlags mFeatureFlags;
-
- private QSPanelController mController;
-
- @Before
- public void setup() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- when(mQSPanel.isAttachedToWindow()).thenReturn(true);
- when(mQSPanel.getDumpableTag()).thenReturn("QSPanel");
- when(mQSPanel.getOrCreateTileLayout()).thenReturn(mPagedTileLayout);
- when(mQSPanel.getTileLayout()).thenReturn(mPagedTileLayout);
- when(mQSPanel.getResources()).thenReturn(mResources);
- when(mResources.getConfiguration()).thenReturn(mConfiguration);
- when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile));
- when(mQSTileHost.createTileView(any(), eq(mQSTile), anyBoolean())).thenReturn(mQSTileView);
- when(mToggleSliderViewControllerFactory.create(any(), any()))
- .thenReturn(mBrightnessSliderController);
- when(mBrightnessControllerFactory.create(any(ToggleSlider.class)))
- .thenReturn(mBrightnessController);
- when(mQSTileRevealControllerFactory.create(any(), any()))
- .thenReturn(mQSTileRevealController);
- when(mMediaHost.getDisappearParameters()).thenReturn(new DisappearParameters());
-
- mController = new QSPanelController(mQSPanel, mQSFgsManagerFooter, mQSSecurityFooter,
- mTunerService, mQSTileHost, mQSCustomizerController, true, mMediaHost,
- mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
- mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory,
- mFalsingManager, mCommandQueue, mFeatureFlags
- );
-
- mController.init();
- }
-
- @Test
- public void testOpenDetailsWithNonExistingTile_NoException() {
- mController.openDetails("none");
-
- verify(mQSPanel, never()).openDetails(any());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index 4ae19332..5213a30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -36,10 +36,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
@@ -64,9 +61,6 @@
private lateinit var mParentView: ViewGroup
@Mock
- private lateinit var mCallback: QSDetail.Callback
-
- @Mock
private lateinit var mQSTileView: QSTileView
private lateinit var mFooter: View
@@ -97,29 +91,10 @@
whenever(mHost.tiles).thenReturn(emptyList())
whenever(mHost.createTileView(any(), any(), anyBoolean())).thenReturn(mQSTileView)
mQsPanel.addTile(mDndTileRecord)
- mQsPanel.setCallback(mCallback)
}
}
@Test
- fun testOpenDetailsWithExistingTile_NoException() {
- mTestableLooper.runWithLooper {
- mQsPanel.openDetails(dndTile)
- }
-
- verify(mCallback).onShowingDetail(any(), anyInt(), anyInt())
- }
-
- @Test
- fun testOpenDetailsWithNullParameter_NoException() {
- mTestableLooper.runWithLooper {
- mQsPanel.openDetails(null)
- }
-
- verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt())
- }
-
- @Test
fun testSecurityFooter_appearsOnBottomOnSplitShade() {
mQsPanel.onConfigurationChanged(getNewOrientationConfig(ORIENTATION_LANDSCAPE))
mQsPanel.switchSecurityFooter(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 8b7346d..30b464b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -53,7 +53,6 @@
import com.android.internal.logging.InstanceId;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSTileHost;
@@ -420,11 +419,5 @@
@Override
public void destroy() {}
-
-
- @Override
- public DetailAdapter getDetailAdapter() {
- return null;
- }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
index 88b133e..0f2c2647 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
@@ -48,7 +48,6 @@
import com.android.systemui.qs.tiles.RotationLockTile
import com.android.systemui.qs.tiles.ScreenRecordTile
import com.android.systemui.qs.tiles.UiModeNightTile
-import com.android.systemui.qs.tiles.UserTile
import com.android.systemui.qs.tiles.WifiTile
import com.android.systemui.qs.tiles.WorkModeTile
import com.android.systemui.util.leak.GarbageMonitor
@@ -76,7 +75,6 @@
"location" to LocationTile::class.java,
"cast" to CastTile::class.java,
"hotspot" to HotspotTile::class.java,
- "user" to UserTile::class.java,
"battery" to BatterySaverTile::class.java,
"saver" to DataSaverTile::class.java,
"night" to NightDisplayTile::class.java,
@@ -115,7 +113,6 @@
@Mock private lateinit var locationTile: LocationTile
@Mock private lateinit var castTile: CastTile
@Mock private lateinit var hotspotTile: HotspotTile
- @Mock private lateinit var userTile: UserTile
@Mock private lateinit var batterySaverTile: BatterySaverTile
@Mock private lateinit var dataSaverTile: DataSaverTile
@Mock private lateinit var nightDisplayTile: NightDisplayTile
@@ -159,7 +156,6 @@
{ locationTile },
{ castTile },
{ hotspotTile },
- { userTile },
{ batterySaverTile },
{ dataSaverTile },
{ nightDisplayTile },
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
index b7fdc1a..8695b29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
@@ -22,11 +22,13 @@
import android.testing.AndroidTestingRunner
import android.view.View
import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PseudoGridView
+import com.android.systemui.qs.QSUserSwitcherEvent
import com.android.systemui.qs.tiles.UserDetailView
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.mockito.any
@@ -62,6 +64,8 @@
private lateinit var launchView: View
@Mock
private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+ @Mock
+ private lateinit var uiEventLogger: UiEventLogger
@Captor
private lateinit var clickCaptor: ArgumentCaptor<DialogInterface.OnClickListener>
@@ -79,6 +83,7 @@
activityStarter,
falsingManager,
dialogLaunchAnimator,
+ uiEventLogger,
{ dialog }
)
}
@@ -87,6 +92,7 @@
fun showDialog_callsDialogShow() {
controller.showDialog(launchView)
verify(dialogLaunchAnimator).showFromView(dialog, launchView)
+ verify(uiEventLogger).log(QSUserSwitcherEvent.QS_USER_DETAIL_OPEN)
}
@Test
@@ -108,10 +114,14 @@
}
@Test
- fun doneButtonSetWithNullHandler() {
+ fun doneButtonLogsCorrectly() {
controller.showDialog(launchView)
- verify(dialog).setPositiveButton(anyInt(), eq(null))
+ verify(dialog).setPositiveButton(anyInt(), capture(clickCaptor))
+
+ clickCaptor.value.onClick(dialog, DialogInterface.BUTTON_NEUTRAL)
+
+ verify(uiEventLogger).log(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE)
}
@Test
@@ -129,6 +139,7 @@
argThat(IntentMatcher(Settings.ACTION_USER_SETTINGS)),
eq(0)
)
+ verify(uiEventLogger).log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
index 218e7db..711187b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
@@ -22,7 +22,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.OverlayPlugin;
import com.android.systemui.plugins.annotations.Requires;
-import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QS.HeightListener;
import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
@@ -99,7 +98,6 @@
@Requires(target = QS.class, version = QS.VERSION)
@Requires(target = HeightListener.class, version = HeightListener.VERSION)
- @Requires(target = DetailAdapter.class, version = DetailAdapter.VERSION)
public static class QSImpl {
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 36d1629f..f6eff82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -110,7 +110,6 @@
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
-import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardAffordanceView;
@@ -273,8 +272,6 @@
@Mock
private IdleHostViewController mIdleHostViewController;
@Mock
- private QSDetailDisplayer mQSDetailDisplayer;
- @Mock
private KeyguardStatusViewComponent mKeyguardStatusViewComponent;
@Mock
private KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
@@ -528,7 +525,6 @@
mCommunalViewComponentFactory,
mIdleViewComponentFactory,
mLockscreenShadeTransitionController,
- mQSDetailDisplayer,
mGroupManager,
mNotificationAreaController,
mAuthController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
index a57f6a1..4a579cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
@@ -27,26 +27,22 @@
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.CommunalStateController
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.ScreenLifecycle
import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.qs.tiles.UserDetailView
import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.LockscreenGestureLogger
-import com.android.systemui.statusbar.phone.NotificationPanelViewController
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.`when`
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import javax.inject.Provider
@SmallTest
@TestableLooper.RunWithLooper
@@ -77,23 +73,14 @@
private lateinit var dozeParameters: DozeParameters
@Mock
- private lateinit var userDetailViewAdapterProvider: Provider<UserDetailView.Adapter>
-
- @Mock
private lateinit var screenOffAnimationController: ScreenOffAnimationController
@Mock
- private lateinit var featureFlags: FeatureFlags
-
- @Mock
private lateinit var userSwitchDialogController: UserSwitchDialogController
@Mock
private lateinit var uiEventLogger: UiEventLogger
- @Mock
- private lateinit var notificationPanelViewController: NotificationPanelViewController
-
private lateinit var view: FrameLayout
private lateinit var testableLooper: TestableLooper
private lateinit var keyguardQsUserSwitchController: KeyguardQsUserSwitchController
@@ -118,16 +105,12 @@
configurationController,
statusBarStateController,
dozeParameters,
- userDetailViewAdapterProvider,
screenOffAnimationController,
- featureFlags,
userSwitchDialogController,
uiEventLogger)
ViewUtils.attachView(view)
testableLooper.processAllMessages()
- keyguardQsUserSwitchController
- .setNotificationPanelViewController(notificationPanelViewController)
`when`(userSwitcherController.keyguardStateController).thenReturn(keyguardStateController)
`when`(userSwitcherController.keyguardStateController.isShowing).thenReturn(true)
keyguardQsUserSwitchController.init()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
index 9dabf69..c344aea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -54,8 +54,8 @@
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.time.FakeSystemClock
import org.junit.Assert.assertEquals
-import org.junit.Assert.assertNotNull
import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
@@ -86,7 +86,6 @@
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
@Mock private lateinit var activityTaskManager: IActivityTaskManager
- @Mock private lateinit var userDetailAdapter: UserSwitcherController.UserDetailAdapter
@Mock private lateinit var telephonyListenerManager: TelephonyListenerManager
@Mock private lateinit var secureSettings: SecureSettings
@Mock private lateinit var falsingManager: FalsingManager
@@ -161,7 +160,6 @@
falsingManager,
telephonyListenerManager,
activityTaskManager,
- userDetailAdapter,
secureSettings,
uiBgExecutor,
interactionJankMonitor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
index d645449..dff77f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.util.condition;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
@@ -24,16 +25,21 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
@@ -46,6 +52,7 @@
private FakeCondition mCondition2;
private FakeCondition mCondition3;
private HashSet<Condition> mConditions;
+ private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private Monitor mConditionMonitor;
@@ -58,7 +65,83 @@
mCondition3 = spy(new FakeCondition());
mConditions = new HashSet<>(Arrays.asList(mCondition1, mCondition2, mCondition3));
- mConditionMonitor = new Monitor(mConditions, null /*callbacks*/);
+ mConditionMonitor = new Monitor(mExecutor, mConditions, null /*callbacks*/);
+ }
+
+ @Test
+ public void testOverridingCondition() {
+ final Condition overridingCondition = Mockito.mock(Condition.class);
+ final Condition regularCondition = Mockito.mock(Condition.class);
+ final Monitor.Callback callback = Mockito.mock(Monitor.Callback.class);
+
+ final Monitor monitor = new Monitor(
+ mExecutor,
+ new HashSet<>(Arrays.asList(overridingCondition, regularCondition)),
+ new HashSet<>(Arrays.asList(callback)));
+
+ when(overridingCondition.isOverridingCondition()).thenReturn(true);
+ when(overridingCondition.isConditionMet()).thenReturn(true);
+ when(regularCondition.isConditionMet()).thenReturn(false);
+
+ final ArgumentCaptor<Condition.Callback> mCallbackCaptor =
+ ArgumentCaptor.forClass(Condition.Callback.class);
+
+ verify(overridingCondition).addCallback(mCallbackCaptor.capture());
+
+ mCallbackCaptor.getValue().onConditionChanged(overridingCondition);
+ mExecutor.runAllReady();
+
+ verify(callback).onConditionsChanged(eq(true));
+ Mockito.clearInvocations(callback);
+
+ when(regularCondition.isConditionMet()).thenReturn(true);
+ when(overridingCondition.isConditionMet()).thenReturn(false);
+
+ mCallbackCaptor.getValue().onConditionChanged(overridingCondition);
+ mExecutor.runAllReady();
+
+ verify(callback).onConditionsChanged(eq(false));
+
+ clearInvocations(callback);
+ monitor.removeCondition(overridingCondition);
+ mExecutor.runAllReady();
+
+ verify(callback).onConditionsChanged(eq(true));
+ }
+
+ /**
+ * Ensures that when multiple overriding conditions are present, it is the aggregate of those
+ * conditions that are considered.
+ */
+ @Test
+ public void testMultipleOverridingConditions() {
+ final Condition overridingCondition = Mockito.mock(Condition.class);
+ final Condition overridingCondition2 = Mockito.mock(Condition.class);
+ final Condition regularCondition = Mockito.mock(Condition.class);
+ final Monitor.Callback callback = Mockito.mock(Monitor.Callback.class);
+
+ final Monitor monitor = new Monitor(
+ mExecutor,
+ new HashSet<>(Arrays.asList(overridingCondition, overridingCondition2,
+ regularCondition)),
+ new HashSet<>(Arrays.asList(callback)));
+
+ when(overridingCondition.isOverridingCondition()).thenReturn(true);
+ when(overridingCondition.isConditionMet()).thenReturn(true);
+ when(overridingCondition2.isOverridingCondition()).thenReturn(true);
+ when(overridingCondition.isConditionMet()).thenReturn(false);
+ when(regularCondition.isConditionMet()).thenReturn(true);
+
+ final ArgumentCaptor<Condition.Callback> mCallbackCaptor =
+ ArgumentCaptor.forClass(Condition.Callback.class);
+
+ verify(overridingCondition).addCallback(mCallbackCaptor.capture());
+
+ mCallbackCaptor.getValue().onConditionChanged(overridingCondition);
+ mExecutor.runAllReady();
+
+ verify(callback).onConditionsChanged(eq(false));
+ Mockito.clearInvocations(callback);
}
@Test
@@ -66,11 +149,13 @@
final Monitor.Callback callback1 =
mock(Monitor.Callback.class);
mConditionMonitor.addCallback(callback1);
+ mExecutor.runAllReady();
mConditions.forEach(condition -> verify(condition).addCallback(any()));
final Monitor.Callback callback2 =
mock(Monitor.Callback.class);
mConditionMonitor.addCallback(callback2);
+ mExecutor.runAllReady();
mConditions.forEach(condition -> verify(condition, times(1)).addCallback(any()));
}
@@ -79,6 +164,7 @@
final Monitor.Callback callback =
mock(Monitor.Callback.class);
mConditionMonitor.addCallback(callback);
+ mExecutor.runAllReady();
verify(callback).onConditionsChanged(false);
}
@@ -86,38 +172,53 @@
public void addCallback_addSecondCallback_reportWithExistingValue() {
final Monitor.Callback callback1 =
mock(Monitor.Callback.class);
- mConditionMonitor.addCallback(callback1);
-
- mConditionMonitor.overrideAllConditionsMet(true);
+ final Condition condition = mock(Condition.class);
+ when(condition.isConditionMet()).thenReturn(true);
+ final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition)),
+ new HashSet<>(Arrays.asList(callback1)));
final Monitor.Callback callback2 =
mock(Monitor.Callback.class);
- mConditionMonitor.addCallback(callback2);
- verify(callback2).onConditionsChanged(true);
+ monitor.addCallback(callback2);
+ mExecutor.runAllReady();
+ verify(callback2).onConditionsChanged(eq(true));
}
@Test
public void addCallback_noConditions_reportAllConditionsMet() {
- final Monitor monitor = new Monitor(new HashSet<>(), null /*callbacks*/);
+ final Monitor monitor = new Monitor(mExecutor, new HashSet<>(), null /*callbacks*/);
final Monitor.Callback callback = mock(Monitor.Callback.class);
monitor.addCallback(callback);
-
+ mExecutor.runAllReady();
verify(callback).onConditionsChanged(true);
}
@Test
public void removeCallback_shouldNoLongerReceiveUpdate() {
+ final Condition condition = mock(Condition.class);
+ final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition)),
+ null);
final Monitor.Callback callback =
mock(Monitor.Callback.class);
- mConditionMonitor.addCallback(callback);
+ monitor.addCallback(callback);
+ monitor.removeCallback(callback);
+ mExecutor.runAllReady();
clearInvocations(callback);
- mConditionMonitor.removeCallback(callback);
- mConditionMonitor.overrideAllConditionsMet(true);
+ final ArgumentCaptor<Condition.Callback> conditionCallbackCaptor =
+ ArgumentCaptor.forClass(Condition.Callback.class);
+ verify(condition).addCallback(conditionCallbackCaptor.capture());
+ final Condition.Callback conditionCallback = conditionCallbackCaptor.getValue();
+
+ when(condition.isConditionMet()).thenReturn(true);
+ conditionCallback.onConditionChanged(condition);
+ mExecutor.runAllReady();
verify(callback, never()).onConditionsChanged(true);
- mConditionMonitor.overrideAllConditionsMet(false);
+ when(condition.isConditionMet()).thenReturn(false);
+ conditionCallback.onConditionChanged(condition);
+ mExecutor.runAllReady();
verify(callback, never()).onConditionsChanged(false);
}
@@ -131,9 +232,11 @@
mConditionMonitor.addCallback(callback2);
mConditionMonitor.removeCallback(callback1);
+ mExecutor.runAllReady();
mConditions.forEach(condition -> verify(condition, never()).removeCallback(any()));
mConditionMonitor.removeCallback(callback2);
+ mExecutor.runAllReady();
mConditions.forEach(condition -> verify(condition).removeCallback(any()));
}
@@ -147,6 +250,7 @@
mCondition1.fakeUpdateCondition(true);
mCondition2.fakeUpdateCondition(true);
mCondition3.fakeUpdateCondition(true);
+ mExecutor.runAllReady();
verify(callback).onConditionsChanged(true);
}
@@ -163,6 +267,7 @@
clearInvocations(callback);
mCondition1.fakeUpdateCondition(false);
+ mExecutor.runAllReady();
verify(callback).onConditionsChanged(false);
}
@@ -171,16 +276,20 @@
final Monitor.Callback callback =
mock(Monitor.Callback.class);
mConditionMonitor.addCallback(callback);
+ mExecutor.runAllReady();
verify(callback).onConditionsChanged(false);
clearInvocations(callback);
mCondition1.fakeUpdateCondition(true);
+ mExecutor.runAllReady();
verify(callback, never()).onConditionsChanged(anyBoolean());
mCondition2.fakeUpdateCondition(true);
+ mExecutor.runAllReady();
verify(callback, never()).onConditionsChanged(anyBoolean());
mCondition3.fakeUpdateCondition(true);
+ mExecutor.runAllReady();
verify(callback).onConditionsChanged(true);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java
index 7fc6b51..9e0f863 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java
@@ -16,7 +16,8 @@
package com.android.systemui.util.condition;
-import static org.mockito.ArgumentMatchers.anyBoolean;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -73,7 +74,8 @@
final Condition.Callback callback2 = mock(Condition.Callback.class);
mCondition.addCallback(callback2);
- verify(callback2).onConditionChanged(mCondition, true);
+ verify(callback2).onConditionChanged(mCondition);
+ assertThat(mCondition.isConditionMet()).isTrue();
}
@Test
@@ -94,7 +96,8 @@
mCondition.addCallback(callback);
mCondition.fakeUpdateCondition(true);
- verify(callback).onConditionChanged(eq(mCondition), eq(true));
+ verify(callback).onConditionChanged(eq(mCondition));
+ assertThat(mCondition.isConditionMet()).isTrue();
}
@Test
@@ -105,7 +108,8 @@
mCondition.addCallback(callback);
mCondition.fakeUpdateCondition(false);
- verify(callback).onConditionChanged(eq(mCondition), eq(false));
+ verify(callback).onConditionChanged(eq(mCondition));
+ assertThat(mCondition.isConditionMet()).isFalse();
}
@Test
@@ -116,7 +120,7 @@
mCondition.addCallback(callback);
mCondition.fakeUpdateCondition(true);
- verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+ verify(callback, never()).onConditionChanged(eq(mCondition));
}
@Test
@@ -127,6 +131,6 @@
mCondition.addCallback(callback);
mCondition.fakeUpdateCondition(false);
- verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+ verify(callback, never()).onConditionChanged(eq(mCondition));
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cf0a027..a478c31 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16140,6 +16140,23 @@
}
/**
+ * Returns package name by pid.
+ */
+ @Override
+ @Nullable
+ public String getPackageNameByPid(int pid) {
+ synchronized (mPidsSelfLocked) {
+ final ProcessRecord app = mPidsSelfLocked.get(pid);
+
+ if (app != null && app.info != null) {
+ return app.info.packageName;
+ }
+
+ return null;
+ }
+ }
+
+ /**
* Sets if the given pid has an overlay UI or not.
*
* @param pid The pid we are setting overlay UI for.
diff --git a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
index 7b76de2..3c780aa 100644
--- a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
@@ -29,6 +29,7 @@
import android.util.Pair;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.am.AppBatteryExemptionTracker.AppBatteryExemptionPolicy;
import com.android.server.am.AppBatteryExemptionTracker.UidBatteryStates;
@@ -454,7 +455,8 @@
super(injector, tracker,
KEY_BG_BATTERY_EXEMPTION_ENABLED, DEFAULT_BG_BATTERY_EXEMPTION_ENABLED,
AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_WINDOW,
- AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_WINDOW_MS);
+ tracker.mContext.getResources()
+ .getInteger(R.integer.config_bg_current_drain_window));
}
@Override
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 99808b6..6492662 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -38,7 +38,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.AppRestrictionController.DEVICE_CONFIG_SUBNAMESPACE_PREFIX;
-import static com.android.server.am.BaseAppStateTracker.ONE_DAY;
import static com.android.server.am.BaseAppStateTracker.ONE_MINUTE;
import android.annotation.NonNull;
@@ -47,6 +46,8 @@
import android.app.ActivityManager.RestrictionLevel;
import android.content.Context;
import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.os.BatteryConsumer;
import android.os.BatteryConsumer.Dimensions;
import android.os.BatteryStatsInternal;
@@ -64,6 +65,7 @@
import android.util.SparseBooleanArray;
import android.util.TimeUtils;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -1025,70 +1027,58 @@
DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_power_components";
/**
- * Default value to {@link #mTrackerEnabled}.
- */
- static final boolean DEFAULT_BG_CURRENT_DRAIN_MONITOR_ENABLED = true;
-
- /**
* Default value to the {@link #INDEX_REGULAR_CURRENT_DRAIN_THRESHOLD} of
* the {@link #mBgCurrentDrainRestrictedBucketThreshold}.
*/
- static final float DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_THRESHOLD =
- isLowRamDeviceStatic() ? 4.0f : 2.0f;
+ final float mDefaultBgCurrentDrainRestrictedBucket;
/**
* Default value to the {@link #INDEX_REGULAR_CURRENT_DRAIN_THRESHOLD} of
* the {@link #mBgCurrentDrainBgRestrictedThreshold}.
*/
- static final float DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_THRESHOLD =
- isLowRamDeviceStatic() ? 8.0f : 4.0f;
+ final float mDefaultBgCurrentDrainBgRestrictedThreshold;
/**
* Default value to {@link #mBgCurrentDrainWindowMs}.
*/
- static final long DEFAULT_BG_CURRENT_DRAIN_WINDOW_MS = ONE_DAY;
+ final long mDefaultBgCurrentDrainWindowMs;
/**
* Default value to the {@link #INDEX_HIGH_CURRENT_DRAIN_THRESHOLD} of
* the {@link #mBgCurrentDrainRestrictedBucketThreshold}.
*/
- static final float DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_HIGH_THRESHOLD =
- isLowRamDeviceStatic() ? 60.0f : 30.0f;
+ final float mDefaultBgCurrentDrainRestrictedBucketHighThreshold;
/**
* Default value to the {@link #INDEX_HIGH_CURRENT_DRAIN_THRESHOLD} of
* the {@link #mBgCurrentDrainBgRestrictedThreshold}.
*/
- static final float DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_HIGH_THRESHOLD =
- isLowRamDeviceStatic() ? 60.0f : 30.0f;
+ final float mDefaultBgCurrentDrainBgRestrictedHighThreshold;
/**
* Default value to {@link #mBgCurrentDrainMediaPlaybackMinDuration}.
*/
- static final long DEFAULT_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION = 30 * ONE_MINUTE;
+ final long mDefaultBgCurrentDrainMediaPlaybackMinDuration;
/**
* Default value to {@link #mBgCurrentDrainLocationMinDuration}.
*/
- static final long DEFAULT_BG_CURRENT_DRAIN_LOCATION_MIN_DURATION = 30 * ONE_MINUTE;
+ final long mDefaultBgCurrentDrainLocationMinDuration;
/**
* Default value to {@link #mBgCurrentDrainEventDurationBasedThresholdEnabled}.
*/
- static final boolean DEFAULT_BG_CURRENT_DRAIN_EVENT_DURATION_BASED_THRESHOLD_ENABLED =
- false;
+ final boolean mDefaultBgCurrentDrainEventDurationBasedThresholdEnabled;
/**
* Default value to {@link #mBgCurrentDrainRestrictedBucketTypes}.
*/
- static final int DEFAULT_BG_CURRENT_DRAIN_TYPES_TO_RESTRICTED_BUCKET =
- BATTERY_USAGE_TYPE_BACKGROUND;
+ final int mDefaultCurrentDrainTypesToRestrictedBucket;
/**
* Default value to {@link #mBgCurrentDrainBgRestrictedTypes}.
*/
- static final int DEFAULT_BG_CURRENT_DRAIN_TYPES_TO_BG_RESTRICTED =
- BATTERY_USAGE_TYPE_BACKGROUND | BATTERY_USAGE_TYPE_FOREGROUND_SERVICE;
+ final int mDefaultBgCurrentDrainTypesToBgRestricted;
/**
* Default value to {@link #mBgCurrentDrainPowerComponents}.
@@ -1096,6 +1086,8 @@
@BatteryConsumer.PowerComponent
static final int DEFAULT_BG_CURRENT_DRAIN_POWER_COMPONENTS = POWER_COMPONENT_ANY;
+ final int mDefaultBgCurrentDrainPowerComponent;
+
/**
* The index to {@link #mBgCurrentDrainRestrictedBucketThreshold}
* and {@link #mBgCurrentDrainBgRestrictedThreshold}.
@@ -1107,36 +1099,28 @@
* @see #KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET.
* @see #KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_RESTRICTED_BUCKET.
*/
- volatile float[] mBgCurrentDrainRestrictedBucketThreshold = {
- DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_THRESHOLD,
- DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_HIGH_THRESHOLD,
- };
+ volatile float[] mBgCurrentDrainRestrictedBucketThreshold = new float[2];
/**
* @see #KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_BG_RESTRICTED.
* @see #KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_BG_RESTRICTED.
*/
- volatile float[] mBgCurrentDrainBgRestrictedThreshold = {
- DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_THRESHOLD,
- DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_HIGH_THRESHOLD,
- };
+ volatile float[] mBgCurrentDrainBgRestrictedThreshold = new float[2];
/**
* @see #KEY_BG_CURRENT_DRAIN_WINDOW.
*/
- volatile long mBgCurrentDrainWindowMs = DEFAULT_BG_CURRENT_DRAIN_WINDOW_MS;
+ volatile long mBgCurrentDrainWindowMs;
/**
* @see #KEY_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION.
*/
- volatile long mBgCurrentDrainMediaPlaybackMinDuration =
- DEFAULT_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION;
+ volatile long mBgCurrentDrainMediaPlaybackMinDuration;
/**
* @see #KEY_BG_CURRENT_DRAIN_LOCATION_MIN_DURATION.
*/
- volatile long mBgCurrentDrainLocationMinDuration =
- DEFAULT_BG_CURRENT_DRAIN_LOCATION_MIN_DURATION;
+ volatile long mBgCurrentDrainLocationMinDuration;
/**
* @see #KEY_BG_CURRENT_DRAIN_EVENT_DURATION_BASED_THRESHOLD_ENABLED.
@@ -1183,8 +1167,62 @@
AppBatteryPolicy(@NonNull Injector injector, @NonNull AppBatteryTracker tracker) {
super(injector, tracker, KEY_BG_CURRENT_DRAIN_MONITOR_ENABLED,
- DEFAULT_BG_CURRENT_DRAIN_MONITOR_ENABLED);
+ tracker.mContext.getResources()
+ .getBoolean(R.bool.config_bg_current_drain_monitor_enabled));
mLock = tracker.mLock;
+ final Resources resources = tracker.mContext.getResources();
+ float[] val = getFloatArray(resources.obtainTypedArray(
+ R.array.config_bg_current_drain_threshold_to_restricted_bucket));
+ mDefaultBgCurrentDrainRestrictedBucket =
+ isLowRamDeviceStatic() ? val[1] : val[0];
+ val = getFloatArray(resources.obtainTypedArray(
+ R.array.config_bg_current_drain_threshold_to_bg_restricted));
+ mDefaultBgCurrentDrainBgRestrictedThreshold =
+ isLowRamDeviceStatic() ? val[1] : val[0];
+ mDefaultBgCurrentDrainWindowMs = resources.getInteger(
+ R.integer.config_bg_current_drain_window);
+ val = getFloatArray(resources.obtainTypedArray(
+ R.array.config_bg_current_drain_high_threshold_to_restricted_bucket));
+ mDefaultBgCurrentDrainRestrictedBucketHighThreshold =
+ isLowRamDeviceStatic() ? val[1] : val[0];
+ val = getFloatArray(resources.obtainTypedArray(
+ R.array.config_bg_current_drain_high_threshold_to_bg_restricted));
+ mDefaultBgCurrentDrainBgRestrictedHighThreshold =
+ isLowRamDeviceStatic() ? val[1] : val[0];
+ mDefaultBgCurrentDrainMediaPlaybackMinDuration = resources.getInteger(
+ R.integer.config_bg_current_drain_media_playback_min_duration);
+ mDefaultBgCurrentDrainLocationMinDuration = resources.getInteger(
+ R.integer.config_bg_current_drain_location_min_duration);
+ mDefaultBgCurrentDrainEventDurationBasedThresholdEnabled = resources.getBoolean(
+ R.bool.config_bg_current_drain_event_duration_based_threshold_enabled);
+ mDefaultCurrentDrainTypesToRestrictedBucket = resources.getInteger(
+ R.integer.config_bg_current_drain_types_to_restricted_bucket);
+ mDefaultBgCurrentDrainTypesToBgRestricted = resources.getInteger(
+ R.integer.config_bg_current_drain_types_to_bg_restricted);
+ mDefaultBgCurrentDrainPowerComponent = resources.getInteger(
+ R.integer.config_bg_current_drain_power_components);
+ mBgCurrentDrainRestrictedBucketThreshold[0] =
+ mDefaultBgCurrentDrainRestrictedBucket;
+ mBgCurrentDrainRestrictedBucketThreshold[1] =
+ mDefaultBgCurrentDrainRestrictedBucketHighThreshold;
+ mBgCurrentDrainBgRestrictedThreshold[0] =
+ mDefaultBgCurrentDrainBgRestrictedThreshold;
+ mBgCurrentDrainBgRestrictedThreshold[1] =
+ mDefaultBgCurrentDrainBgRestrictedHighThreshold;
+ mBgCurrentDrainWindowMs = mDefaultBgCurrentDrainWindowMs;
+ mBgCurrentDrainMediaPlaybackMinDuration =
+ mDefaultBgCurrentDrainMediaPlaybackMinDuration;
+ mBgCurrentDrainLocationMinDuration = mDefaultBgCurrentDrainLocationMinDuration;
+ }
+
+ static float[] getFloatArray(TypedArray array) {
+ int length = array.length();
+ float[] floatArray = new float[length];
+ for (int i = 0; i < length; i++) {
+ floatArray[i] = array.getFloat(i, Float.NaN);
+ }
+ array.recycle();
+ return floatArray;
}
@Override
@@ -1234,31 +1272,31 @@
mBgCurrentDrainRestrictedBucketThreshold[INDEX_REGULAR_CURRENT_DRAIN_THRESHOLD] =
DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET,
- DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_THRESHOLD);
+ mDefaultBgCurrentDrainRestrictedBucket);
mBgCurrentDrainRestrictedBucketThreshold[INDEX_HIGH_CURRENT_DRAIN_THRESHOLD] =
DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_RESTRICTED_BUCKET,
- DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_HIGH_THRESHOLD);
+ mDefaultBgCurrentDrainRestrictedBucketHighThreshold);
mBgCurrentDrainBgRestrictedThreshold[INDEX_REGULAR_CURRENT_DRAIN_THRESHOLD] =
DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_BG_RESTRICTED,
- DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_THRESHOLD);
+ mDefaultBgCurrentDrainBgRestrictedThreshold);
mBgCurrentDrainBgRestrictedThreshold[INDEX_HIGH_CURRENT_DRAIN_THRESHOLD] =
DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_BG_RESTRICTED,
- DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_HIGH_THRESHOLD);
+ mDefaultBgCurrentDrainBgRestrictedHighThreshold);
mBgCurrentDrainRestrictedBucketTypes =
DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_BG_CURRENT_DRAIN_TYPES_TO_RESTRICTED_BUCKET,
- DEFAULT_BG_CURRENT_DRAIN_TYPES_TO_RESTRICTED_BUCKET);
+ mDefaultCurrentDrainTypesToRestrictedBucket);
mBgCurrentDrainBgRestrictedTypes =
DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_BG_CURRENT_DRAIN_TYPES_TO_BG_RESTRICTED,
- DEFAULT_BG_CURRENT_DRAIN_TYPES_TO_BG_RESTRICTED);
+ mDefaultBgCurrentDrainTypesToBgRestricted);
mBgCurrentDrainPowerComponents =
DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_BG_CURRENT_DRAIN_POWER_COMPONENTS,
- DEFAULT_BG_CURRENT_DRAIN_POWER_COMPONENTS);
+ mDefaultBgCurrentDrainPowerComponent);
if (mBgCurrentDrainPowerComponents == DEFAULT_BG_CURRENT_DRAIN_POWER_COMPONENTS) {
mBatteryDimensions = BatteryUsage.BATT_DIMENS;
} else {
@@ -1273,28 +1311,28 @@
mBgCurrentDrainWindowMs = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_BG_CURRENT_DRAIN_WINDOW,
- DEFAULT_BG_CURRENT_DRAIN_WINDOW_MS);
+ mDefaultBgCurrentDrainWindowMs);
}
private void updateCurrentDrainMediaPlaybackMinDuration() {
mBgCurrentDrainMediaPlaybackMinDuration = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION,
- DEFAULT_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION);
+ mDefaultBgCurrentDrainMediaPlaybackMinDuration);
}
private void updateCurrentDrainLocationMinDuration() {
mBgCurrentDrainLocationMinDuration = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_BG_CURRENT_DRAIN_LOCATION_MIN_DURATION,
- DEFAULT_BG_CURRENT_DRAIN_LOCATION_MIN_DURATION);
+ mDefaultBgCurrentDrainLocationMinDuration);
}
private void updateCurrentDrainEventDurationBasedThresholdEnabled() {
mBgCurrentDrainEventDurationBasedThresholdEnabled = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_BG_CURRENT_DRAIN_EVENT_DURATION_BASED_THRESHOLD_ENABLED,
- DEFAULT_BG_CURRENT_DRAIN_EVENT_DURATION_BASED_THRESHOLD_ENABLED);
+ mDefaultBgCurrentDrainEventDurationBasedThresholdEnabled);
}
@Override
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index cedc7db..d2c6c13 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2968,8 +2968,9 @@
int step;
// skip a2dp absolute volume control request when the device
- // is not an a2dp device
- if (!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
+ // is neither an a2dp device nor BLE device
+ if ((!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
+ && !AudioSystem.DEVICE_OUT_ALL_BLE_SET.contains(device))
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) {
return;
}
@@ -3107,7 +3108,8 @@
}
if (device == AudioSystem.DEVICE_OUT_BLE_HEADSET
- && streamType == getBluetoothContextualVolumeStream()) {
+ && streamType == getBluetoothContextualVolumeStream()
+ && (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
if (DEBUG_VOL) {
Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index="
+ newIndex + " stream=" + streamType);
@@ -3707,8 +3709,9 @@
int oldIndex;
// skip a2dp absolute volume control request when the device
- // is not an a2dp device
- if (!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
+ // is neither an a2dp device nor BLE device
+ if ((!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
+ && !AudioSystem.DEVICE_OUT_ALL_BLE_SET.contains(device))
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) {
return;
}
@@ -3751,7 +3754,8 @@
}
if (device == AudioSystem.DEVICE_OUT_BLE_HEADSET
- && streamType == getBluetoothContextualVolumeStream()) {
+ && streamType == getBluetoothContextualVolumeStream()
+ && (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
if (DEBUG_VOL) {
Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index="
+ index + " stream=" + streamType);
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index 603f206..108e7bc 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -25,7 +25,6 @@
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES;
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH;
import static com.android.server.net.NetworkPolicyManagerService.OPPORTUNISTIC_QUOTA_UNKNOWN;
@@ -191,6 +190,7 @@
class MultipathTracker {
final Network network;
final String subscriberId;
+ private final int mSubId;
private long mQuota;
/** Current multipath budget. Nonzero iff we have budget and a UsageCallback is armed. */
@@ -204,9 +204,8 @@
this.network = network;
this.mNetworkCapabilities = new NetworkCapabilities(nc);
NetworkSpecifier specifier = nc.getNetworkSpecifier();
- int subId = INVALID_SUBSCRIPTION_ID;
if (specifier instanceof TelephonyNetworkSpecifier) {
- subId = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
+ mSubId = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
} else {
throw new IllegalStateException(String.format(
"Can't get subId from mobile network %s (%s)",
@@ -217,14 +216,14 @@
if (tele == null) {
throw new IllegalStateException(String.format("Missing TelephonyManager"));
}
- tele = tele.createForSubscriptionId(subId);
+ tele = tele.createForSubscriptionId(mSubId);
if (tele == null) {
throw new IllegalStateException(String.format(
- "Can't get TelephonyManager for subId %d", subId));
+ "Can't get TelephonyManager for subId %d", mSubId));
}
subscriberId = Objects.requireNonNull(tele.getSubscriberId(),
- "Null subscriber Id for subId " + subId);
+ "Null subscriber Id for subId " + mSubId);
mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
.setSubscriberIds(Set.of(subscriberId))
.setMeteredness(NetworkStats.METERED_YES)
@@ -282,6 +281,7 @@
.setSubscriberId(subscriberId)
.setRoaming(!nc.hasCapability(NET_CAPABILITY_NOT_ROAMING))
.setMetered(!nc.hasCapability(NET_CAPABILITY_NOT_METERED))
+ .setSubId(mSubId)
.build();
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index d71e07a..418e91d 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -75,6 +75,7 @@
import com.android.server.policy.WindowManagerPolicy;
import java.io.PrintWriter;
+import java.util.Objects;
/**
* Controls the power state of the display.
@@ -236,42 +237,42 @@
// True if we should fade the screen while turning it off, false if we should play
// a stylish color fade animation instead.
- private boolean mColorFadeFadesConfig;
+ private final boolean mColorFadeFadesConfig;
// True if we need to fake a transition to off when coming out of a doze state.
// Some display hardware will blank itself when coming out of doze in order to hide
// artifacts. For these displays we fake a transition into OFF so that policy can appropriately
// blank itself and begin an appropriate power on animation.
- private boolean mDisplayBlanksAfterDozeConfig;
+ private final boolean mDisplayBlanksAfterDozeConfig;
// True if there are only buckets of brightness values when the display is in the doze state,
// rather than a full range of values. If this is true, then we'll avoid animating the screen
// brightness since it'd likely be multiple jarring brightness transitions instead of just one
// to reach the final state.
- private boolean mBrightnessBucketsInDozeConfig;
+ private final boolean mBrightnessBucketsInDozeConfig;
// The pending power request.
// Initially null until the first call to requestPowerState.
- // Guarded by mLock.
+ @GuardedBy("mLock")
private DisplayPowerRequest mPendingRequestLocked;
// True if a request has been made to wait for the proximity sensor to go negative.
- // Guarded by mLock.
+ @GuardedBy("mLock")
private boolean mPendingWaitForNegativeProximityLocked;
// True if the pending power request or wait for negative proximity flag
// has been changed since the last update occurred.
- // Guarded by mLock.
+ @GuardedBy("mLock")
private boolean mPendingRequestChangedLocked;
// Set to true when the important parts of the pending power request have been applied.
// The important parts are mainly the screen state. Brightness changes may occur
// concurrently.
- // Guarded by mLock.
+ @GuardedBy("mLock")
private boolean mDisplayReadyLocked;
// Set to true if a power state update is required.
- // Guarded by mLock.
+ @GuardedBy("mLock")
private boolean mPendingUpdatePowerStateLocked;
/* The following state must only be accessed by the handler thread. */
@@ -352,8 +353,8 @@
// information.
// At the time of this writing, this value is changed within updatePowerState() only, which is
// limited to the thread used by DisplayControllerHandler.
- private BrightnessReason mBrightnessReason = new BrightnessReason();
- private BrightnessReason mBrightnessReasonTemp = new BrightnessReason();
+ private final BrightnessReason mBrightnessReason = new BrightnessReason();
+ private final BrightnessReason mBrightnessReasonTemp = new BrightnessReason();
// Brightness animation ramp rates in brightness units per second
private float mBrightnessRampRateFastDecrease;
@@ -849,6 +850,7 @@
}
}
+ @GuardedBy("mLock")
private void sendUpdatePowerStateLocked() {
if (!mStopped && !mPendingUpdatePowerStateLocked) {
mPendingUpdatePowerStateLocked = true;
@@ -2318,6 +2320,7 @@
return mAutomaticBrightnessController.convertToNits(brightness);
}
+ @GuardedBy("mLock")
private void updatePendingProximityRequestsLocked() {
mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
mPendingWaitForNegativeProximityLocked = false;
@@ -2421,12 +2424,7 @@
pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
- mHandler.runWithScissors(new Runnable() {
- @Override
- public void run() {
- dumpLocal(pw);
- }
- }, 1000);
+ mHandler.runWithScissors(() -> dumpLocal(pw), 1000);
}
private void dumpLocal(PrintWriter pw) {
@@ -2458,6 +2456,9 @@
pw.println(" mAppliedThrottling=" + mAppliedThrottling);
pw.println(" mAppliedScreenBrightnessOverride=" + mAppliedScreenBrightnessOverride);
pw.println(" mAppliedTemporaryBrightness=" + mAppliedTemporaryBrightness);
+ pw.println(" mAppliedTemporaryAutoBrightnessAdjustment="
+ + mAppliedTemporaryAutoBrightnessAdjustment);
+ pw.println(" mAppliedBrightnessBoost=" + mAppliedBrightnessBoost);
pw.println(" mDozing=" + mDozing);
pw.println(" mSkipRampState=" + skipRampStateToString(mSkipRampState));
pw.println(" mScreenOnBlockStartRealTime=" + mScreenOnBlockStartRealTime);
@@ -2465,21 +2466,21 @@
pw.println(" mPendingScreenOnUnblocker=" + mPendingScreenOnUnblocker);
pw.println(" mPendingScreenOffUnblocker=" + mPendingScreenOffUnblocker);
pw.println(" mPendingScreenOff=" + mPendingScreenOff);
- pw.println(" mReportedToPolicy=" +
- reportedToPolicyToString(mReportedScreenStateToPolicy));
+ pw.println(" mReportedToPolicy="
+ + reportedToPolicyToString(mReportedScreenStateToPolicy));
if (mScreenBrightnessRampAnimator != null) {
- pw.println(" mScreenBrightnessRampAnimator.isAnimating()=" +
- mScreenBrightnessRampAnimator.isAnimating());
+ pw.println(" mScreenBrightnessRampAnimator.isAnimating()="
+ + mScreenBrightnessRampAnimator.isAnimating());
}
if (mColorFadeOnAnimator != null) {
- pw.println(" mColorFadeOnAnimator.isStarted()=" +
- mColorFadeOnAnimator.isStarted());
+ pw.println(" mColorFadeOnAnimator.isStarted()="
+ + mColorFadeOnAnimator.isStarted());
}
if (mColorFadeOffAnimator != null) {
- pw.println(" mColorFadeOffAnimator.isStarted()=" +
- mColorFadeOffAnimator.isStarted());
+ pw.println(" mColorFadeOffAnimator.isStarted()="
+ + mColorFadeOffAnimator.isStarted());
}
if (mPowerState != null) {
@@ -2605,7 +2606,7 @@
}
}
- private final void logHbmBrightnessStats(float brightness, int displayStatsId) {
+ private void logHbmBrightnessStats(float brightness, int displayStatsId) {
synchronized (mHandler) {
FrameworkStatsLog.write(
FrameworkStatsLog.DISPLAY_HBM_BRIGHTNESS_CHANGED, displayStatsId, brightness);
@@ -2824,7 +2825,7 @@
@Override
public boolean equals(Object obj) {
- if (obj == null || !(obj instanceof BrightnessReason)) {
+ if (!(obj instanceof BrightnessReason)) {
return false;
}
BrightnessReason other = (BrightnessReason) obj;
@@ -2832,6 +2833,11 @@
}
@Override
+ public int hashCode() {
+ return Objects.hash(reason, modifier);
+ }
+
+ @Override
public String toString() {
return toString(0);
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index c02e725..d233c5e 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -182,8 +182,11 @@
private final long mPhysicalDisplayId;
private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>();
private final ArrayList<Integer> mSupportedColorModes = new ArrayList<>();
+ private final DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs =
+ new DisplayModeDirector.DesiredDisplayModeSpecs();
private final boolean mIsDefaultDisplay;
private final BacklightAdapter mBacklightAdapter;
+ private final SidekickInternal mSidekickInternal;
private DisplayDeviceInfo mInfo;
private boolean mHavePendingChanges;
@@ -200,8 +203,6 @@
private int mActiveDisplayModeAtStartId = INVALID_MODE_ID;
private Display.Mode mUserPreferredMode;
private int mActiveModeId = INVALID_MODE_ID;
- private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs =
- new DisplayModeDirector.DesiredDisplayModeSpecs();
private boolean mDisplayModeSpecsInvalid;
private int mActiveColorMode;
private Display.HdrCapabilities mHdrCapabilities;
@@ -210,13 +211,11 @@
private boolean mAllmRequested;
private boolean mGameContentTypeRequested;
private boolean mSidekickActive;
- private SidekickInternal mSidekickInternal;
private SurfaceControl.StaticDisplayInfo mStaticDisplayInfo;
// The supported display modes according to SurfaceFlinger
private SurfaceControl.DisplayMode[] mSfDisplayModes;
// The active display mode in SurfaceFlinger
private SurfaceControl.DisplayMode mActiveSfDisplayMode;
- private DisplayDeviceConfig mDisplayDeviceConfig;
private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides =
new DisplayEventReceiver.FrameRateOverride[0];
@@ -233,7 +232,6 @@
mSidekickInternal = LocalServices.getService(SidekickInternal.class);
mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay,
mSurfaceControlProxy);
- mDisplayDeviceConfig = null;
mActiveDisplayModeAtStartId = dynamicInfo.activeDisplayModeId;
}
@@ -459,9 +457,6 @@
final Context context = getOverlayContext();
mDisplayDeviceConfig = DisplayDeviceConfig.create(context, mPhysicalDisplayId,
mIsDefaultDisplay);
- if (mDisplayDeviceConfig == null) {
- return;
- }
// Load brightness HWC quirk
mBacklightAdapter.setForceSurfaceControl(mDisplayDeviceConfig.hasQuirk(
@@ -1083,8 +1078,8 @@
pw.println("mGameContentTypeRequested=" + mGameContentTypeRequested);
pw.println("mStaticDisplayInfo=" + mStaticDisplayInfo);
pw.println("mSfDisplayModes=");
- for (int i = 0; i < mSfDisplayModes.length; i++) {
- pw.println(" " + mSfDisplayModes[i]);
+ for (SurfaceControl.DisplayMode sfDisplayMode : mSfDisplayModes) {
+ pw.println(" " + sfDisplayMode);
}
pw.println("mActiveSfDisplayMode=" + mActiveSfDisplayMode);
pw.println("mSupportedModes=");
@@ -1238,6 +1233,8 @@
}
public static class Injector {
+ // Native callback.
+ @SuppressWarnings("unused")
private ProxyDisplayEventReceiver mReceiver;
public void setDisplayEventListenerLocked(Looper looper, DisplayEventListener listener) {
mReceiver = new ProxyDisplayEventReceiver(looper, listener);
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 93c73be..6f5729f 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -825,7 +825,6 @@
*
* @param device The device to associate with the LogicalDisplay.
* @param displayId The display ID to give the new display. If invalid, a new ID is assigned.
- * @param isDefault Indicates if we are creating the default display.
* @return The new logical display if created, null otherwise.
*/
private LogicalDisplay createNewLogicalDisplayLocked(DisplayDevice device, int displayId) {
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index d8672fc..2567e43 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -55,7 +55,7 @@
* If this is the first time the property is being set or if the rate is 0,
* the value jumps directly to the target.
*
- * @param target The target value.
+ * @param targetLinear The target value.
* @param rate The convergence rate in units per second, or 0 to set the value immediately.
* @return True if the target differs from the previous target.
*/
diff --git a/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java b/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java
new file mode 100644
index 0000000..6b442a6
--- /dev/null
+++ b/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.logcat;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.os.ServiceManager;
+import android.os.logcat.ILogcatManagerService;
+import android.util.Slog;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+
+/**
+ * This dialog is shown to the user before an activity in a harmful app is launched.
+ *
+ * See {@code PackageManager.setLogcatAppInfo} for more info.
+ */
+public class LogAccessConfirmationActivity extends AlertActivity implements
+ DialogInterface.OnClickListener {
+ private static final String TAG = LogAccessConfirmationActivity.class.getSimpleName();
+
+ private String mPackageName;
+ private IntentSender mTarget;
+ private final ILogcatManagerService mLogcatManagerService =
+ ILogcatManagerService.Stub.asInterface(ServiceManager.getService("logcat"));
+
+ private int mUid;
+ private int mGid;
+ private int mPid;
+ private int mFd;
+
+ private static final String EXTRA_UID = "uid";
+ private static final String EXTRA_GID = "gid";
+ private static final String EXTRA_PID = "pid";
+ private static final String EXTRA_FD = "fd";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Intent intent = getIntent();
+ mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ mUid = intent.getIntExtra("uid", 0);
+ mGid = intent.getIntExtra("gid", 0);
+ mPid = intent.getIntExtra("pid", 0);
+ mFd = intent.getIntExtra("fd", 0);
+
+ final AlertController.AlertParams p = mAlertParams;
+ p.mTitle = getString(R.string.log_access_confirmation_title);
+ p.mView = createView();
+
+ p.mPositiveButtonText = getString(R.string.log_access_confirmation_allow);
+ p.mPositiveButtonListener = this;
+ p.mNegativeButtonText = getString(R.string.log_access_confirmation_deny);
+ p.mNegativeButtonListener = this;
+
+ mAlert.installContent(mAlertParams);
+ }
+
+ private View createView() {
+ final View view = getLayoutInflater().inflate(R.layout.harmful_app_warning_dialog,
+ null /*root*/);
+ ((TextView) view.findViewById(R.id.app_name_text))
+ .setText(mPackageName);
+ ((TextView) view.findViewById(R.id.message))
+ .setText(getIntent().getExtras().getString("body"));
+ return view;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ try {
+ mLogcatManagerService.approve(mUid, mGid, mPid, mFd);
+ } catch (Throwable t) {
+ Slog.e(TAG, "Could not start the LogcatManagerService.", t);
+ }
+ finish();
+ break;
+ case DialogInterface.BUTTON_NEGATIVE:
+ try {
+ mLogcatManagerService.decline(mUid, mGid, mPid, mFd);
+ } catch (Throwable t) {
+ Slog.e(TAG, "Could not start the LogcatManagerService.", t);
+ }
+ finish();
+ break;
+ }
+ }
+
+ /**
+ * Create the Intent for a LogAccessConfirmationActivity.
+ */
+ public static Intent createIntent(Context context, String targetPackageName,
+ IntentSender target, int uid, int gid, int pid, int fd) {
+ final Intent intent = new Intent();
+ intent.setClass(context, LogAccessConfirmationActivity.class);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, targetPackageName);
+ intent.putExtra(EXTRA_UID, uid);
+ intent.putExtra(EXTRA_GID, gid);
+ intent.putExtra(EXTRA_PID, pid);
+ intent.putExtra(EXTRA_FD, fd);
+
+ return intent;
+ }
+
+}
diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java
index ff6372ae..34614d5 100644
--- a/services/core/java/com/android/server/logcat/LogcatManagerService.java
+++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java
@@ -16,20 +16,36 @@
package com.android.server.logcat;
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.ActivityManagerInternal;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.os.ILogd;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.os.logcat.ILogcatManagerService;
import android.util.Slog;
+import com.android.internal.R;
+import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
+import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
- * Service responsible for manage the access to Logcat.
+ * Service responsible for managing the access to Logcat.
*/
public final class LogcatManagerService extends SystemService {
@@ -38,6 +54,43 @@
private final BinderService mBinderService;
private final ExecutorService mThreadExecutor;
private ILogd mLogdService;
+ private NotificationManager mNotificationManager;
+ private @NonNull ActivityManager mActivityManager;
+ private ActivityManagerInternal mActivityManagerInternal;
+ private static final int MAX_UID_IMPORTANCE_COUNT_LISTENER = 2;
+ private static int sUidImportanceListenerCount = 0;
+ private static final int AID_SHELL_UID = 2000;
+
+ // TODO This allowlist is just a temporary workaround for the tests:
+ // FrameworksServicesTests
+ // PlatformRuleTests
+ // After adapting the test suites, the allowlist will be removed in
+ // the upcoming bug fix patches.
+ private static final String[] ALLOWABLE_TESTING_PACKAGES = {
+ "android.platform.test.rule.tests",
+ "com.android.frameworks.servicestests"
+ };
+
+ // TODO Same as the above ALLOWABLE_TESTING_PACKAGES.
+ private boolean isAllowableTestingPackage(int uid) {
+ PackageManager pm = mContext.getPackageManager();
+
+ String[] packageNames = pm.getPackagesForUid(uid);
+
+ if (ArrayUtils.isEmpty(packageNames)) {
+ return false;
+ }
+
+ for (String name : packageNames) {
+ Slog.e(TAG, "isAllowableTestingPackage: " + name);
+
+ if (Arrays.asList(ALLOWABLE_TESTING_PACKAGES).contains(name)) {
+ return true;
+ }
+ }
+
+ return false;
+ };
private final class BinderService extends ILogcatManagerService.Stub {
@Override
@@ -51,6 +104,197 @@
// the logd data access is finished.
mThreadExecutor.execute(new LogdMonitor(uid, gid, pid, fd, false));
}
+
+ @Override
+ public void approve(int uid, int gid, int pid, int fd) {
+ try {
+ getLogdService().approve(uid, gid, pid, fd);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void decline(int uid, int gid, int pid, int fd) {
+ try {
+ getLogdService().decline(uid, gid, pid, fd);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private ILogd getLogdService() {
+ synchronized (LogcatManagerService.this) {
+ if (mLogdService == null) {
+ LogcatManagerService.this.addLogdService();
+ }
+ return mLogdService;
+ }
+ }
+
+ private String getBodyString(Context context, String callingPackage, int uid) {
+ PackageManager pm = context.getPackageManager();
+ try {
+ return context.getString(
+ com.android.internal.R.string.log_access_confirmation_body,
+ pm.getApplicationInfoAsUser(callingPackage, PackageManager.MATCH_DIRECT_BOOT_AUTO,
+ UserHandle.getUserId(uid)).loadLabel(pm));
+ } catch (NameNotFoundException e) {
+ // App name is unknown.
+ return null;
+ }
+ }
+
+ private void sendNotification(int notificationId, String clientInfo, int uid, int gid, int pid,
+ int fd) {
+
+ final ActivityManagerInternal activityManagerInternal =
+ LocalServices.getService(ActivityManagerInternal.class);
+
+ PackageManager pm = mContext.getPackageManager();
+ String packageName = activityManagerInternal.getPackageNameByPid(pid);
+ if (packageName != null) {
+ String notificationBody = getBodyString(mContext, packageName, uid);
+
+ final Intent mIntent = LogAccessConfirmationActivity.createIntent(mContext,
+ packageName, null, uid, gid, pid, fd);
+
+ if (notificationBody == null) {
+ // Decline the logd access if the nofitication body is unknown
+ Slog.e(TAG, "Unknown notification body, declining the logd access");
+ declineLogdAccess(uid, gid, pid, fd);
+ return;
+ }
+
+ // TODO Next version will replace notification with dialogue
+ // per UX guidance.
+ generateNotificationWithBodyContent(notificationId, clientInfo, notificationBody,
+ mIntent);
+ return;
+
+ }
+
+ String[] packageNames = pm.getPackagesForUid(uid);
+
+ if (ArrayUtils.isEmpty(packageNames)) {
+ // Decline the logd access if the app name is unknown
+ Slog.e(TAG, "Unknown calling package name, declining the logd access");
+ declineLogdAccess(uid, gid, pid, fd);
+ return;
+ }
+
+ String firstPackageName = packageNames[0];
+
+ if (firstPackageName == null || firstPackageName.length() == 0) {
+ // Decline the logd access if the package name from uid is unknown
+ Slog.e(TAG, "Unknown calling package name, declining the logd access");
+ declineLogdAccess(uid, gid, pid, fd);
+ return;
+ }
+
+ String notificationBody = getBodyString(mContext, firstPackageName, uid);
+
+ final Intent mIntent = LogAccessConfirmationActivity.createIntent(mContext,
+ firstPackageName, null, uid, gid, pid, fd);
+
+ if (notificationBody == null) {
+ Slog.e(TAG, "Unknown notification body, declining the logd access");
+ declineLogdAccess(uid, gid, pid, fd);
+ return;
+ }
+
+ // TODO Next version will replace notification with dialogue
+ // per UX guidance.
+ generateNotificationWithBodyContent(notificationId, clientInfo,
+ notificationBody, mIntent);
+ }
+
+ private void declineLogdAccess(int uid, int gid, int pid, int fd) {
+ try {
+ getLogdService().decline(uid, gid, pid, fd);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Fails to call remote functions ", ex);
+ }
+ }
+
+ private void generateNotificationWithBodyContent(int notificationId, String clientInfo,
+ String notificationBody, Intent intent) {
+ final Notification.Builder notificationBuilder = new Notification.Builder(
+ mContext,
+ SystemNotificationChannels.ACCESSIBILITY_SECURITY_POLICY);
+ intent.setFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.setIdentifier(String.valueOf(notificationId) + clientInfo);
+ intent.putExtra("body", notificationBody);
+
+ notificationBuilder
+ .setSmallIcon(R.drawable.ic_info)
+ .setContentTitle(
+ mContext.getString(R.string.log_access_confirmation_title))
+ .setContentText(notificationBody)
+ .setContentIntent(
+ PendingIntent.getActivity(mContext, 0, intent,
+ PendingIntent.FLAG_IMMUTABLE))
+ .setTicker(mContext.getString(R.string.log_access_confirmation_title))
+ .setOnlyAlertOnce(true)
+ .setAutoCancel(true);
+ mNotificationManager.notify(notificationId, notificationBuilder.build());
+ }
+
+ /**
+ * A class which watches an uid for background access and notifies the logdMonitor when
+ * the package status becomes foreground (importance change)
+ */
+ private class UidImportanceListener implements ActivityManager.OnUidImportanceListener {
+ private final int mExpectedUid;
+ private final int mExpectedGid;
+ private final int mExpectedPid;
+ private final int mExpectedFd;
+ private int mExpectedImportance;
+ private int mCurrentImportance = RunningAppProcessInfo.IMPORTANCE_GONE;
+
+ UidImportanceListener(int uid, int gid, int pid, int fd, int importance) {
+ mExpectedUid = uid;
+ mExpectedGid = gid;
+ mExpectedPid = pid;
+ mExpectedFd = fd;
+ mExpectedImportance = importance;
+ }
+
+ @Override
+ public void onUidImportance(int uid, int importance) {
+ if (uid == mExpectedUid) {
+ mCurrentImportance = importance;
+
+ /**
+ * 1) If the process status changes to foreground, send a notification
+ * for user consent.
+ * 2) If the process status remains background, we decline logd access request.
+ **/
+ if (importance <= RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE) {
+ String clientInfo = getClientInfo(uid, mExpectedGid, mExpectedPid, mExpectedFd);
+ sendNotification(0, clientInfo, uid, mExpectedGid, mExpectedPid,
+ mExpectedFd);
+ mActivityManager.removeOnUidImportanceListener(this);
+
+ synchronized (LogcatManagerService.this) {
+ sUidImportanceListenerCount--;
+ }
+ } else {
+ try {
+ getLogdService().decline(uid, mExpectedGid, mExpectedPid, mExpectedFd);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Fails to call remote functions ", ex);
+ }
+ }
+ }
+ }
+ }
+
+ private static String getClientInfo(int uid, int gid, int pid, int fd) {
+ return "UID=" + Integer.toString(uid) + " GID=" + Integer.toString(gid) + " PID="
+ + Integer.toString(pid) + " FD=" + Integer.toString(fd);
}
private class LogdMonitor implements Runnable {
@@ -74,9 +318,7 @@
}
/**
- * The current version grant the permission by default.
- * And track the logd access.
- * The next version will generate a prompt for users.
+ * LogdMonitor generates a prompt for users.
* The users decide whether the logd access is allowed.
*/
@Override
@@ -86,11 +328,14 @@
}
if (mStart) {
+
+ // TODO Temporarily approve all the requests to unblock testing failures.
try {
- mLogdService.approve(mUid, mGid, mPid, mFd);
- } catch (RemoteException ex) {
- Slog.e(TAG, "Fails to call remote functions ", ex);
+ getLogdService().approve(mUid, mGid, mPid, mFd);
+ } catch (RemoteException e) {
+ e.printStackTrace();
}
+ return;
}
}
}
@@ -100,6 +345,8 @@
mContext = context;
mBinderService = new BinderService();
mThreadExecutor = Executors.newCachedThreadPool();
+ mActivityManager = context.getSystemService(ActivityManager.class);
+ mNotificationManager = mContext.getSystemService(NotificationManager.class);
}
@Override
@@ -114,5 +361,4 @@
private void addLogdService() {
mLogdService = ILogd.Stub.asInterface(ServiceManager.getService("logd"));
}
-
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 240ed77..8d05415 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -63,7 +63,6 @@
import static android.net.INetd.FIREWALL_RULE_DENY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -176,11 +175,9 @@
import android.net.NetworkPolicyManager;
import android.net.NetworkPolicyManager.UidState;
import android.net.NetworkRequest;
-import android.net.NetworkSpecifier;
import android.net.NetworkStack;
import android.net.NetworkStateSnapshot;
import android.net.NetworkTemplate;
-import android.net.TelephonyNetworkSpecifier;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.BestClock;
@@ -1520,7 +1517,8 @@
.setType(TYPE_MOBILE)
.setSubscriberId(subscriberId)
.setMetered(true)
- .setDefaultNetwork(true).build();
+ .setDefaultNetwork(true)
+ .setSubId(subId).build();
if (template.matches(probeIdent)) {
return subId;
}
@@ -1757,7 +1755,8 @@
.setType(TYPE_MOBILE)
.setSubscriberId(subscriberId)
.setMetered(true)
- .setDefaultNetwork(true).build();
+ .setDefaultNetwork(true)
+ .setSubId(subId).build();
for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
final NetworkTemplate template = mNetworkPolicy.keyAt(i);
if (template.matches(probeIdent)) {
@@ -1989,7 +1988,8 @@
.setType(TYPE_MOBILE)
.setSubscriberId(subscriberId)
.setMetered(true)
- .setDefaultNetwork(true).build();
+ .setDefaultNetwork(true)
+ .setSubId(subId).build();
// Template is matched when subscriber id matches.
if (template.matches(probeIdent)) {
matchingSubIds.add(subId);
@@ -2091,7 +2091,8 @@
mNetIdToSubId.clear();
final ArrayMap<NetworkStateSnapshot, NetworkIdentity> identified = new ArrayMap<>();
for (final NetworkStateSnapshot snapshot : snapshots) {
- mNetIdToSubId.put(snapshot.getNetwork().getNetId(), parseSubId(snapshot));
+ final int subId = snapshot.getSubId();
+ mNetIdToSubId.put(snapshot.getNetwork().getNetId(), subId);
// Policies matched by NPMS only match by subscriber ID or by network ID.
final NetworkIdentity ident = new NetworkIdentity.Builder()
@@ -2296,7 +2297,8 @@
.setType(TYPE_MOBILE)
.setSubscriberId(subscriberId)
.setMetered(true)
- .setDefaultNetwork(true).build();
+ .setDefaultNetwork(true)
+ .setSubId(subId).build();
for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
final NetworkTemplate template = mNetworkPolicy.keyAt(i);
if (template.matches(probeIdent)) {
@@ -5919,17 +5921,6 @@
}
}
- private int parseSubId(@NonNull NetworkStateSnapshot snapshot) {
- int subId = INVALID_SUBSCRIPTION_ID;
- if (snapshot.getNetworkCapabilities().hasTransport(TRANSPORT_CELLULAR)) {
- NetworkSpecifier spec = snapshot.getNetworkCapabilities().getNetworkSpecifier();
- if (spec instanceof TelephonyNetworkSpecifier) {
- subId = ((TelephonyNetworkSpecifier) spec).getSubscriptionId();
- }
- }
- return subId;
- }
-
@GuardedBy("mNetworkPoliciesSecondLock")
private int getSubIdLocked(Network network) {
return mNetIdToSubId.get(network.getNetId(), INVALID_SUBSCRIPTION_ID);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2717f0c..3d34976 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5805,6 +5805,7 @@
|| channel.isImportanceLockedByCriticalDeviceFunction());
final StatusBarNotification adjustedSbn = notificationRecord.getSbn();
userId = adjustedSbn.getUser().getIdentifier();
+ int uid = adjustedSbn.getUid();
ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
if (summaries == null) {
summaries = new ArrayMap<>();
@@ -5851,7 +5852,7 @@
notificationRecord.getIsAppImportanceLocked());
summaries.put(pkg, summarySbn.getKey());
}
- if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
+ if (summaryRecord != null && checkDisqualifyingFeatures(userId, uid,
summaryRecord.getSbn().getId(), summaryRecord.getSbn().getTag(), summaryRecord,
true)) {
return summaryRecord;
@@ -6527,7 +6528,7 @@
@VisibleForTesting
protected void fixNotification(Notification notification, String pkg, String tag, int id,
- int userId) throws NameNotFoundException {
+ int userId) throws NameNotFoundException, RemoteException {
final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
(userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
@@ -6561,6 +6562,21 @@
actions.toArray(notification.actions);
}
+ // Ensure MediaStyle has correct permissions for remote device extras
+ if (notification.isStyle(Notification.MediaStyle.class)) {
+ int hasMediaContentControlPermission = mPackageManager.checkPermission(
+ android.Manifest.permission.MEDIA_CONTENT_CONTROL, pkg, userId);
+ if (hasMediaContentControlPermission != PERMISSION_GRANTED) {
+ notification.extras.remove(Notification.EXTRA_MEDIA_REMOTE_DEVICE);
+ notification.extras.remove(Notification.EXTRA_MEDIA_REMOTE_ICON);
+ notification.extras.remove(Notification.EXTRA_MEDIA_REMOTE_INTENT);
+ if (DBG) {
+ Slog.w(TAG, "Package " + pkg + ": Use of setRemotePlayback requires the "
+ + "MEDIA_CONTENT_CONTROL permission");
+ }
+ }
+ }
+
// Remote views? Are they too big?
checkRemoteViews(pkg, tag, id, notification);
}
@@ -6822,7 +6838,6 @@
return false;
}
-
// blocked apps
boolean isBlocked = !areNotificationsEnabledForPackageInt(pkg, uid);
synchronized (mNotificationLock) {
@@ -7215,10 +7230,12 @@
if (mAssistants.isEnabled()) {
mAssistants.onNotificationEnqueuedLocked(r);
mHandler.postDelayed(
- new PostNotificationRunnable(r.getKey(), enqueueElapsedTimeMs),
+ new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+ r.getUid(), enqueueElapsedTimeMs),
DELAY_FOR_ASSISTANT_TIME);
} else {
- mHandler.post(new PostNotificationRunnable(r.getKey(), enqueueElapsedTimeMs));
+ mHandler.post(new PostNotificationRunnable(r.getKey(),
+ r.getSbn().getPackageName(), r.getUid(), enqueueElapsedTimeMs));
}
}
}
@@ -7242,16 +7259,19 @@
protected class PostNotificationRunnable implements Runnable {
private final String key;
private final long postElapsedTimeMs;
+ private final String pkg;
+ private final int uid;
- PostNotificationRunnable(String key, @ElapsedRealtimeLong long postElapsedTimeMs) {
+ PostNotificationRunnable(String key, String pkg, int uid,
+ @ElapsedRealtimeLong long postElapsedTimeMs) {
this.key = key;
+ this.pkg = pkg;
+ this.uid = uid;
this.postElapsedTimeMs = postElapsedTimeMs;
}
@Override
public void run() {
- String pkg = StatusBarNotification.getPkgFromKey(key);
- int uid = StatusBarNotification.getUidFromKey(key);
boolean appBanned = !areNotificationsEnabledForPackageInt(pkg, uid);
synchronized (mNotificationLock) {
try {
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
index c2b3cbc..0320818 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
@@ -20,6 +20,8 @@
import android.annotation.Nullable;
import android.content.pm.ActivityInfo;
+import java.util.Set;
+
/** @hide **/
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
public interface ParsedActivity extends ParsedMainComponent {
@@ -59,6 +61,12 @@
@Nullable
String getPermission();
+ /**
+ * Gets the trusted host certificates of apps that are allowed to embed this activity.
+ */
+ @NonNull
+ Set<String> getKnownActivityEmbeddingCerts();
+
int getPersistableMode();
int getPrivateFlags();
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
index 91c0b07..acd5a81 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
@@ -23,6 +23,7 @@
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import static com.android.server.pm.pkg.parsing.ParsingPackageImpl.sForInternedString;
+import static com.android.server.pm.pkg.parsing.ParsingPackageImpl.sForStringSet;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -33,12 +34,17 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
import com.android.server.pm.pkg.parsing.ParsingUtils;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.Set;
+
/**
* @hide
**/
@@ -63,6 +69,8 @@
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
private String permission;
+ @Nullable
+ private Set<String> mKnownActivityEmbeddingCerts;
private int launchMode;
private int documentLaunchMode;
@@ -113,6 +121,7 @@
this.rotationAnimation = other.rotationAnimation;
this.colorMode = other.colorMode;
this.windowLayout = other.windowLayout;
+ this.mKnownActivityEmbeddingCerts = other.mKnownActivityEmbeddingCerts;
}
/**
@@ -239,6 +248,25 @@
return this;
}
+ @NonNull
+ @Override
+ public Set<String> getKnownActivityEmbeddingCerts() {
+ return mKnownActivityEmbeddingCerts == null ? Collections.emptySet()
+ : mKnownActivityEmbeddingCerts;
+ }
+
+ /**
+ * Sets the trusted host certificates of apps that are allowed to embed this activity.
+ */
+ public void setKnownActivityEmbeddingCerts(@NonNull Set<String> knownActivityEmbeddingCerts) {
+ // Convert the provided digest to upper case for consistent Set membership
+ // checks when verifying the signing certificate digests of requesting apps.
+ this.mKnownActivityEmbeddingCerts = new ArraySet<>();
+ for (String knownCert : knownActivityEmbeddingCerts) {
+ this.mKnownActivityEmbeddingCerts.add(knownCert.toUpperCase(Locale.US));
+ }
+ }
+
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("Activity{");
@@ -287,6 +315,7 @@
} else {
dest.writeBoolean(false);
}
+ sForStringSet.parcel(this.mKnownActivityEmbeddingCerts, dest, flags);
}
public ParsedActivityImpl() {
@@ -320,6 +349,7 @@
if (in.readBoolean()) {
windowLayout = new ActivityInfo.WindowLayout(in);
}
+ this.mKnownActivityEmbeddingCerts = sForStringSet.unparcel(in);
}
@NonNull
@@ -344,7 +374,7 @@
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -360,6 +390,7 @@
@Nullable String taskAffinity,
int privateFlags,
@Nullable String permission,
+ @Nullable Set<String> knownActivityEmbeddingCerts,
int launchMode,
int documentLaunchMode,
int maxRecents,
@@ -383,6 +414,7 @@
this.taskAffinity = taskAffinity;
this.privateFlags = privateFlags;
this.permission = permission;
+ this.mKnownActivityEmbeddingCerts = knownActivityEmbeddingCerts;
this.launchMode = launchMode;
this.documentLaunchMode = documentLaunchMode;
this.maxRecents = maxRecents;
@@ -645,10 +677,10 @@
}
@DataClass.Generated(
- time = 1641431949361L,
+ time = 1644372875433L,
codegenVersion = "1.0.23",
- sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java",
- inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull android.content.pm.parsing.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull android.content.pm.parsing.component.ParsedActivityImpl makeAlias(java.lang.String,android.content.pm.parsing.component.ParsedActivity)\npublic android.content.pm.parsing.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic android.content.pm.parsing.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic android.content.pm.parsing.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic android.content.pm.parsing.component.ParsedActivityImpl setPermission(java.lang.String)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+ sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java",
+ inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.server.pm.pkg.component.ParsedActivity)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.server.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
index a6c22a18..bbbf598 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
@@ -21,6 +21,7 @@
import static com.android.server.pm.pkg.component.ComponentParseUtils.flag;
import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET;
+import static com.android.server.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -151,7 +152,8 @@
| flag(ActivityInfo.FLAG_SHOW_WHEN_LOCKED, R.styleable.AndroidManifestActivity_showWhenLocked, sa)
| flag(ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE, R.styleable.AndroidManifestActivity_supportsPictureInPicture, sa)
| flag(ActivityInfo.FLAG_TURN_SCREEN_ON, R.styleable.AndroidManifestActivity_turnScreenOn, sa)
- | flag(ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING, R.styleable.AndroidManifestActivity_preferMinimalPostProcessing, sa)));
+ | flag(ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING, R.styleable.AndroidManifestActivity_preferMinimalPostProcessing, sa))
+ | flag(ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING, R.styleable.AndroidManifestActivity_allowUntrustedActivityEmbedding, sa));
activity.setPrivateFlags(activity.getPrivateFlags() | (flag(ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED,
R.styleable.AndroidManifestActivity_inheritShowWhenLocked, sa)
@@ -338,6 +340,20 @@
activity.setPermission(permission != null ? permission : pkg.getPermission());
}
+ final ParseResult<Set<String>> knownActivityEmbeddingCertsResult =
+ parseKnownActivityEmbeddingCerts(array, resources, isAlias
+ ? R.styleable.AndroidManifestActivityAlias_knownActivityEmbeddingCerts
+ : R.styleable.AndroidManifestActivity_knownActivityEmbeddingCerts, input);
+ if (knownActivityEmbeddingCertsResult.isError()) {
+ return input.error(knownActivityEmbeddingCertsResult);
+ } else {
+ final Set<String> knownActivityEmbeddingCerts = knownActivityEmbeddingCertsResult
+ .getResult();
+ if (knownActivityEmbeddingCerts != null) {
+ activity.setKnownActivityEmbeddingCerts(knownActivityEmbeddingCerts);
+ }
+ }
+
final boolean setExported = array.hasValue(exportedAttr);
if (setExported) {
activity.setExported(array.getBoolean(exportedAttr, false));
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
index b92b845..f199841 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
@@ -549,6 +549,7 @@
ai.metaData = a.getMetaData();
}
ai.applicationInfo = applicationInfo;
+ ai.setKnownActivityEmbeddingCerts(a.getKnownActivityEmbeddingCerts());
return ai;
}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
index 52d9b7a..1754877 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
@@ -381,6 +381,12 @@
ParsingPackage setLocaleConfigRes(int localeConfigRes);
+ /**
+ * Sets the trusted host certificates of apps that are allowed to embed activities of this
+ * application.
+ */
+ ParsingPackage setKnownActivityEmbeddingCerts(Set<String> knownActivityEmbeddingCerts);
+
// TODO(b/135203078): This class no longer has access to ParsedPackage, find a replacement
// for moving to the next step
@CallSuper
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
index 84422cd..c4b1275 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
@@ -563,6 +563,9 @@
return (mBooleans & flag) != 0;
}
+ @Nullable
+ private Set<String> mKnownActivityEmbeddingCerts;
+
// Derived fields
@NonNull
private UUID mStorageUuid;
@@ -1150,6 +1153,9 @@
appInfo.setVersionCode(mLongVersionCode);
appInfo.setAppClassNamesByProcess(buildAppClassNamesByProcess());
appInfo.setLocaleConfigRes(mLocaleConfigRes);
+ if (mKnownActivityEmbeddingCerts != null) {
+ appInfo.setKnownActivityEmbeddingCerts(mKnownActivityEmbeddingCerts);
+ }
return appInfo;
}
@@ -1329,6 +1335,7 @@
dest.writeInt(this.nativeHeapZeroInitialized);
sForBoolean.parcel(this.requestRawExternalStorageAccess, dest, flags);
dest.writeInt(this.mLocaleConfigRes);
+ sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags);
}
public ParsingPackageImpl(Parcel in) {
@@ -1477,6 +1484,7 @@
this.nativeHeapZeroInitialized = in.readInt();
this.requestRawExternalStorageAccess = sForBoolean.unparcel(in);
this.mLocaleConfigRes = in.readInt();
+ this.mKnownActivityEmbeddingCerts = sForStringSet.unparcel(in);
assignDerivedFields();
}
@@ -2376,6 +2384,13 @@
return getBoolean(Booleans.RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED);
}
+ @NonNull
+ @Override
+ public Set<String> getKnownActivityEmbeddingCerts() {
+ return mKnownActivityEmbeddingCerts == null ? Collections.emptySet()
+ : mKnownActivityEmbeddingCerts;
+ }
+
@Override
public boolean shouldInheritKeyStoreKeys() {
return getBoolean(Booleans.INHERIT_KEYSTORE_KEYS);
@@ -2973,4 +2988,11 @@
mLocaleConfigRes = value;
return this;
}
+
+ @Override
+ public ParsingPackage setKnownActivityEmbeddingCerts(
+ @Nullable Set<String> knownEmbeddingCerts) {
+ mKnownActivityEmbeddingCerts = knownEmbeddingCerts;
+ return this;
+ }
}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index e8f03ff..a891980 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -30,6 +30,8 @@
import static android.os.Build.VERSION_CODES.O;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static com.android.server.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts;
+
import android.annotation.AnyRes;
import android.annotation.CheckResult;
import android.annotation.IntDef;
@@ -2009,6 +2011,19 @@
.AndroidManifestApplication_requestForegroundServiceExemption,
false));
}
+ final ParseResult<Set<String>> knownActivityEmbeddingCertsResult =
+ parseKnownActivityEmbeddingCerts(sa, res,
+ R.styleable.AndroidManifestApplication_knownActivityEmbeddingCerts,
+ input);
+ if (knownActivityEmbeddingCertsResult.isError()) {
+ return input.error(knownActivityEmbeddingCertsResult);
+ } else {
+ final Set<String> knownActivityEmbeddingCerts = knownActivityEmbeddingCertsResult
+ .getResult();
+ if (knownActivityEmbeddingCerts != null) {
+ pkg.setKnownActivityEmbeddingCerts(knownActivityEmbeddingCerts);
+ }
+ }
} finally {
sa.recycle();
}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
index 9430e98..cb474df 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
@@ -22,6 +22,8 @@
import android.annotation.Nullable;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.os.Parcel;
import android.os.Parcelable;
@@ -38,6 +40,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/** @hide **/
public class ParsingUtils {
@@ -168,4 +171,50 @@
return list;
}
}
+
+ /**
+ * Parse the {@link android.R.attr#knownActivityEmbeddingCerts} attribute, if available.
+ */
+ @NonNull
+ public static ParseResult<Set<String>> parseKnownActivityEmbeddingCerts(@NonNull TypedArray sa,
+ @NonNull Resources res, int resourceId, @NonNull ParseInput input) {
+ if (!sa.hasValue(resourceId)) {
+ return input.success(null);
+ }
+
+ final int knownActivityEmbeddingCertsResource = sa.getResourceId(resourceId, 0);
+ if (knownActivityEmbeddingCertsResource != 0) {
+ // The knownCerts attribute supports both a string array resource as well as a
+ // string resource for the case where the permission should only be granted to a
+ // single known signer.
+ Set<String> knownEmbeddingCertificates = null;
+ final String resourceType = res.getResourceTypeName(
+ knownActivityEmbeddingCertsResource);
+ if (resourceType.equals("array")) {
+ final String[] knownCerts = res.getStringArray(knownActivityEmbeddingCertsResource);
+ if (knownCerts != null) {
+ knownEmbeddingCertificates = Set.of(knownCerts);
+ }
+ } else {
+ final String knownCert = res.getString(knownActivityEmbeddingCertsResource);
+ if (knownCert != null) {
+ knownEmbeddingCertificates = Set.of(knownCert);
+ }
+ }
+ if (knownEmbeddingCertificates == null || knownEmbeddingCertificates.isEmpty()) {
+ return input.error("Defined a knownActivityEmbeddingCerts attribute but the "
+ + "provided resource is null");
+ }
+ return input.success(knownEmbeddingCertificates);
+ }
+
+ // If the knownCerts resource ID is null - the app specified a string value for the
+ // attribute representing a single trusted signer.
+ final String knownCert = sa.getString(resourceId);
+ if (knownCert == null || knownCert.isEmpty()) {
+ return input.error("Defined a knownActivityEmbeddingCerts attribute but the provided "
+ + "string is empty");
+ }
+ return input.success(Set.of(knownCert));
+ }
}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStateAppInfo.java b/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStateAppInfo.java
index 2ef90ac..3205b76 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStateAppInfo.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStateAppInfo.java
@@ -21,6 +21,8 @@
import android.content.pm.ApplicationInfo;
import android.util.SparseArray;
+import java.util.Set;
+
/**
* Container for fields that are eventually exposed through {@link ApplicationInfo}.
* <p>
@@ -285,6 +287,13 @@
String getPermission();
/**
+ * @see ApplicationInfo#knownActivityEmbeddingCerts
+ * @see R.styleable#AndroidManifestApplication_knownActivityEmbeddingCerts
+ */
+ @NonNull
+ Set<String> getKnownActivityEmbeddingCerts();
+
+ /**
* @see ApplicationInfo#processName
* @see R.styleable#AndroidManifestApplication_process
*/
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 49f759d..adee325 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -4491,6 +4491,9 @@
}
int pullMediaCapabilitiesStats(int atomTag, List<StatsEvent> pulledData) {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
+ return StatsManager.PULL_SKIP;
+ }
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
if (audioManager == null) {
return StatsManager.PULL_SKIP;
diff --git a/services/core/java/com/android/server/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
index 27c0bee..10e868d 100644
--- a/services/core/java/com/android/server/tracing/TracingServiceProxy.java
+++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
@@ -15,6 +15,13 @@
*/
package com.android.server.tracing;
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT;
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_BEGIN;
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_BIND_PERM_INCORRECT;
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_COMM_ERROR;
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_HANDOFF;
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_PERM_MISSING;
+
import android.Manifest;
import android.annotation.NonNull;
import android.content.ComponentName;
@@ -39,6 +46,7 @@
import android.util.Slog;
import com.android.internal.infra.ServiceConnector;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.SystemService;
import java.io.IOException;
@@ -71,6 +79,17 @@
private static final String INTENT_ACTION_NOTIFY_SESSION_STOLEN =
"com.android.traceur.NOTIFY_SESSION_STOLEN";
+ private static final int REPORT_BEGIN =
+ TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_BEGIN;
+ private static final int REPORT_SVC_HANDOFF =
+ TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_HANDOFF;
+ private static final int REPORT_BIND_PERM_INCORRECT =
+ TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_BIND_PERM_INCORRECT;
+ private static final int REPORT_SVC_PERM_MISSING =
+ TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_PERM_MISSING;
+ private static final int REPORT_SVC_COMM_ERROR =
+ TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_COMM_ERROR;
+
private final Context mContext;
private final PackageManager mPackageManager;
private final LruCache<ComponentName, ServiceConnector<IMessenger>> mCachedReporterServices;
@@ -134,17 +153,24 @@
}
private void reportTrace(@NonNull TraceReportParams params) {
+ FrameworkStatsLog.write(TRACING_SERVICE_REPORT_EVENT, REPORT_BEGIN,
+ params.uuidLsb, params.uuidMsb);
+
// We don't need to do any permission checks on the caller because access
// to this service is guarded by SELinux.
ComponentName component = new ComponentName(params.reporterPackageName,
params.reporterClassName);
if (!hasBindServicePermission(component)) {
+ FrameworkStatsLog.write(TRACING_SERVICE_REPORT_EVENT, REPORT_BIND_PERM_INCORRECT,
+ params.uuidLsb, params.uuidMsb);
return;
}
boolean hasDumpPermission = hasPermission(component, Manifest.permission.DUMP);
boolean hasUsageStatsPermission = hasPermission(component,
Manifest.permission.PACKAGE_USAGE_STATS);
if (!hasDumpPermission || !hasUsageStatsPermission) {
+ FrameworkStatsLog.write(TRACING_SERVICE_REPORT_EVENT, REPORT_SVC_PERM_MISSING,
+ params.uuidLsb, params.uuidMsb);
return;
}
final long ident = Binder.clearCallingIdentity();
@@ -178,8 +204,13 @@
message.what = TraceReportService.MSG_REPORT_TRACE;
message.obj = params;
messenger.send(message);
+
+ FrameworkStatsLog.write(TRACING_SERVICE_REPORT_EVENT, REPORT_SVC_HANDOFF,
+ params.uuidLsb, params.uuidMsb);
}).whenComplete((res, err) -> {
if (err != null) {
+ FrameworkStatsLog.write(TRACING_SERVICE_REPORT_EVENT, REPORT_SVC_COMM_ERROR,
+ params.uuidLsb, params.uuidMsb);
Slog.e(TAG, "Failed to report trace", err);
}
try {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 56985af..87c8a79 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -429,7 +429,8 @@
if (needsExtraction) {
extractColors(wallpaper);
}
- notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId);
+ notifyColorListeners(getAdjustedWallpaperColorsOnDimming(wallpaper), which,
+ wallpaper.userId, displayId);
}
private static <T extends IInterface> boolean emptyCallbackList(RemoteCallbackList<T> list) {
@@ -1519,7 +1520,6 @@
if (mWallpaper.mWallpaperDimAmount != 0f) {
try {
connector.mEngine.applyDimming(mWallpaper.mWallpaperDimAmount);
- notifyWallpaperColorsChanged(mWallpaper, FLAG_SYSTEM);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to dim wallpaper", e);
}
@@ -2711,8 +2711,31 @@
extractColors(wallpaperData);
}
+ return getAdjustedWallpaperColorsOnDimming(wallpaperData);
+ }
+
+ /**
+ * Gets the adjusted {@link WallpaperColors} if the wallpaper colors were not extracted from
+ * bitmap (i.e. it's a live wallpaper) and the dim amount is not 0. If these conditions apply,
+ * default to using color hints that do not support dark theme and dark text.
+ *
+ * @param wallpaperData WallpaperData containing the WallpaperColors and mWallpaperDimAmount
+ */
+ WallpaperColors getAdjustedWallpaperColorsOnDimming(WallpaperData wallpaperData) {
synchronized (mLock) {
- return wallpaperData.primaryColors;
+ WallpaperColors wallpaperColors = wallpaperData.primaryColors;
+
+ if (wallpaperColors != null
+ && (wallpaperColors.getColorHints() & WallpaperColors.HINT_FROM_BITMAP) == 0
+ && wallpaperData.mWallpaperDimAmount != 0f) {
+ int adjustedColorHints = wallpaperColors.getColorHints()
+ & ~WallpaperColors.HINT_SUPPORTS_DARK_TEXT
+ & ~WallpaperColors.HINT_SUPPORTS_DARK_THEME;
+ return new WallpaperColors(
+ wallpaperColors.getPrimaryColor(), wallpaperColors.getSecondaryColor(),
+ wallpaperColors.getTertiaryColor(), adjustedColorHints);
+ }
+ return wallpaperColors;
}
}
@@ -2782,6 +2805,7 @@
wallpaper.fromForegroundApp = fromForegroundApp;
wallpaper.cropHint.set(cropHint);
wallpaper.allowBackup = allowBackup;
+ wallpaper.mWallpaperDimAmount = getWallpaperDimAmount();
}
return pfd;
} finally {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index ec4c58f..d526845 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1088,7 +1088,7 @@
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
if (r != null && r.isState(RESUMED, PAUSING)) {
r.mDisplayContent.mAppTransition.overridePendingAppTransition(
- packageName, enterAnim, exitAnim, null, null,
+ packageName, enterAnim, exitAnim, backgroundColor, null, null,
r.mOverrideTaskTransition);
r.mTransitionController.setOverrideAnimation(
TransitionInfo.AnimationOptions.makeCustomAnimOptions(packageName,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index fd58936..6ef6c7a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4516,6 +4516,7 @@
pendingOptions.getPackageName(),
pendingOptions.getCustomEnterResId(),
pendingOptions.getCustomExitResId(),
+ pendingOptions.getCustomBackgroundColor(),
pendingOptions.getAnimationStartedListener(),
pendingOptions.getAnimationFinishedListener(),
pendingOptions.getOverrideTaskTransition());
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 5164bf0..fb3d17a 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2003,8 +2003,8 @@
* @param targetTask the target task for launching activity, which could be different from
* the one who hosting the embedding.
*/
- private boolean canEmbedActivity(@NonNull TaskFragment taskFragment, ActivityRecord starting,
- boolean newTask, Task targetTask) {
+ private boolean canEmbedActivity(@NonNull TaskFragment taskFragment,
+ @NonNull ActivityRecord starting, boolean newTask, Task targetTask) {
final Task hostTask = taskFragment.getTask();
if (hostTask == null) {
return false;
@@ -2016,8 +2016,7 @@
return true;
}
- // Not allowed embedding an activity of another app.
- if (hostUid != starting.getUid()) {
+ if (!taskFragment.isAllowedToEmbedActivity(starting)) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/AnimationAdapter.java b/services/core/java/com/android/server/wm/AnimationAdapter.java
index 5899a4e..a743091 100644
--- a/services/core/java/com/android/server/wm/AnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/AnimationAdapter.java
@@ -42,6 +42,22 @@
boolean getShowWallpaper();
/**
+ * @return Whether we should show a background behind the animating windows.
+ * @see Animation#getShowBackground()
+ */
+ default boolean getShowBackground() {
+ return false;
+ }
+
+ /**
+ * @return The background color to use during an animation if getShowBackground returns true.
+ * @see Animation#getBackgroundColor()
+ */
+ default int getBackgroundColor() {
+ return 0;
+ }
+
+ /**
* Requests to start the animation.
*
* @param animationLeash The surface to run the animation on. See {@link SurfaceAnimator} for an
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 56adcfd..05efb29 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -93,6 +93,7 @@
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
+import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
@@ -198,6 +199,7 @@
private IRemoteCallback mAnimationFinishedCallback;
private int mNextAppTransitionEnter;
private int mNextAppTransitionExit;
+ private @ColorInt int mNextAppTransitionBackgroundColor;
private int mNextAppTransitionInPlace;
private boolean mNextAppTransitionIsSync;
@@ -829,6 +831,7 @@
break;
}
a = animAttr != 0 ? loadAnimationAttr(lp, animAttr, transit) : null;
+
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b "
+ "Callers=%s",
@@ -836,6 +839,11 @@
Debug.getCallers(3));
}
setAppTransitionFinishedCallbackIfNeeded(a);
+
+ if (mNextAppTransitionBackgroundColor != 0) {
+ a.setBackgroundColor(mNextAppTransitionBackgroundColor);
+ }
+
return a;
}
@@ -861,14 +869,15 @@
}
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
- IRemoteCallback startedCallback, IRemoteCallback endedCallback,
- boolean overrideTaskTransaction) {
+ @ColorInt int backgroundColor, IRemoteCallback startedCallback,
+ IRemoteCallback endedCallback, boolean overrideTaskTransaction) {
if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionOverrideRequested = true;
mNextAppTransitionPackage = packageName;
mNextAppTransitionEnter = enterAnim;
mNextAppTransitionExit = exitAnim;
+ mNextAppTransitionBackgroundColor = backgroundColor;
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
mAnimationFinishedCallback = endedCallback;
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 5a2cf17..afa4f19 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -544,6 +544,11 @@
return getActivityType() == ACTIVITY_TYPE_RECENTS;
}
+ final boolean isActivityTypeHomeOrRecents() {
+ final int activityType = getActivityType();
+ return activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
+ }
+
public boolean isActivityTypeAssistant() {
return getActivityType() == ACTIVITY_TYPE_ASSISTANT;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2c58d92..818e4f6 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4083,7 +4083,7 @@
final boolean renewImeSurface = mImeScreenshot == null
|| mImeScreenshot.getWidth() != mInputMethodWindow.getFrame().width()
|| mImeScreenshot.getHeight() != mInputMethodWindow.getFrame().height();
- if (task != null && !task.isHomeOrRecentsRootTask()) {
+ if (task != null && !task.isActivityTypeHomeOrRecents()) {
SurfaceControl.ScreenshotHardwareBuffer imeBuffer = renewImeSurface
? mWmService.mTaskSnapshotController.snapshotImeFromAttachedTask(task)
: null;
diff --git a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
index a3eb980..61f9fe2 100644
--- a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
@@ -51,6 +51,16 @@
}
@Override
+ public boolean getShowBackground() {
+ return mSpec.getShowBackground();
+ }
+
+ @Override
+ public int getBackgroundColor() {
+ return mSpec.getBackgroundColor();
+ }
+
+ @Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
@AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) {
mAnimator.startAnimation(mSpec, animationLeash, t,
@@ -97,6 +107,20 @@
}
/**
+ * @see AnimationAdapter#getShowBackground
+ */
+ default boolean getShowBackground() {
+ return false;
+ }
+
+ /**
+ * @see AnimationAdapter#getBackgroundColor
+ */
+ default int getBackgroundColor() {
+ return 0;
+ }
+
+ /**
* @see AnimationAdapter#getStatusBarTransitionsStartTime
*/
default long calculateStatusBarTransitionStartTime() {
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index d4a7a5d..e259238 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -529,7 +529,7 @@
*/
void notifyTaskPersisterLocked(Task task, boolean flush) {
final Task rootTask = task != null ? task.getRootTask() : null;
- if (rootTask != null && rootTask.isHomeOrRecentsRootTask()) {
+ if (rootTask != null && rootTask.isActivityTypeHomeOrRecents()) {
// Never persist the home or recents task.
return;
}
@@ -563,7 +563,7 @@
private static boolean shouldPersistTaskLocked(Task task) {
final Task rootTask = task.getRootTask();
- return task.isPersistable && (rootTask == null || !rootTask.isHomeOrRecentsRootTask());
+ return task.isPersistable && (rootTask == null || !rootTask.isActivityTypeHomeOrRecents());
}
void onSystemReadyLocked() {
@@ -994,7 +994,7 @@
}
final Task rootTask = task.getRootTask();
if ((task.isPersistable || task.inRecents)
- && (rootTask == null || !rootTask.isHomeOrRecentsRootTask())) {
+ && (rootTask == null || !rootTask.isActivityTypeHomeOrRecents())) {
if (TaskPersister.DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
persistentTaskIds.add(task.mTaskId);
} else {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index c1835a0..30906e5 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -613,7 +613,7 @@
final TaskFragment adjacentTask = task.getRootTask().getAdjacentTaskFragment();
final boolean inSplitScreen = task.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW
&& adjacentTask != null;
- if (task.isHomeOrRecentsRootTask()
+ if (task.isActivityTypeHomeOrRecents()
// Skip if the task is in split screen and in landscape.
|| (inSplitScreen && isDisplayLandscape)
// Skip if the task is the top task in split screen.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b84ef77..6df54cd 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2711,7 +2711,7 @@
// they extend past their root task and sysui uses the root task surface to control
// cropping.
// TODO(b/158242495): get rid of this when drag/drop can use surface bounds.
- if (isActivityTypeHome() || isActivityTypeRecents()) {
+ if (isActivityTypeHomeOrRecents()) {
// Make sure this is the top-most non-organizer root task (if not top-most, it means
// another translucent task could be above this, so this needs to stay cropped.
final Task rootTask = getRootTask();
@@ -3306,7 +3306,7 @@
if (control != null) {
// We let the transition to be controlled by RecentsAnimation, and callback task's
// RemoteAnimationTarget for remote runner to animate.
- if (enter && !isHomeOrRecentsRootTask()) {
+ if (enter && !isActivityTypeHomeOrRecents()) {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
"applyAnimationUnchecked, control: %s, task: %s, transit: %s",
control, asTask(), AppTransition.appTransitionOldToString(transit));
@@ -4591,10 +4591,6 @@
!PRESERVE_WINDOWS);
}
- final boolean isHomeOrRecentsRootTask() {
- return isActivityTypeHome() || isActivityTypeRecents();
- }
-
final boolean isOnHomeDisplay() {
return getDisplayId() == DEFAULT_DISPLAY;
}
@@ -5044,7 +5040,7 @@
// The transition animation and starting window are not needed if {@code allowMoveToFront}
// is false, because the activity won't be visible.
- if ((!isHomeOrRecentsRootTask() || hasActivity()) && allowMoveToFront) {
+ if ((!isActivityTypeHomeOrRecents() || hasActivity()) && allowMoveToFront) {
final DisplayContent dc = mDisplayContent;
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
"Prepare open transition: starting " + r);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index a59d7b6..c880aba 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -25,6 +25,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -95,6 +96,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledPredicate;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -102,6 +104,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -505,6 +508,56 @@
}
/**
+ * Checks if the organized task fragment is allowed to have the specified activity, which is
+ * allowed if an activity allows embedding in untrusted mode, or if the trusted mode can be
+ * enabled.
+ * @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
+ */
+ boolean isAllowedToEmbedActivity(@NonNull ActivityRecord a) {
+ if ((a.info.flags & FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING)
+ == FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING) {
+ return true;
+ }
+
+ return isAllowedToEmbedActivityInTrustedMode(a);
+ }
+
+ /**
+ * Checks if the organized task fragment is allowed to embed activity in fully trusted mode,
+ * which means that all transactions are allowed. This is supported in the following cases:
+ * <li>the activity belongs to the same app as the organizer host;</li>
+ * <li>the activity has declared the organizer host as trusted explicitly via known
+ * certificate.</li>
+ */
+ private boolean isAllowedToEmbedActivityInTrustedMode(@NonNull ActivityRecord a) {
+ if (mTaskFragmentOrganizerUid == a.getUid()) {
+ // Activities from the same UID can be embedded freely by the host.
+ return true;
+ }
+
+ Set<String> knownActivityEmbeddingCerts = a.info.getKnownActivityEmbeddingCerts();
+ if (knownActivityEmbeddingCerts.isEmpty()) {
+ // An application must either declare that it allows untrusted embedding, or specify
+ // a set of app certificates that are allowed to embed it in trusted mode.
+ return false;
+ }
+
+ AndroidPackage hostPackage = mAtmService.getPackageManagerInternalLocked()
+ .getPackage(mTaskFragmentOrganizerUid);
+
+ return hostPackage != null && hostPackage.getSigningDetails().hasAncestorOrSelfWithDigest(
+ knownActivityEmbeddingCerts);
+ }
+
+ /**
+ * Checks if all activities in the task fragment are allowed to be embedded in trusted mode.
+ * @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
+ */
+ boolean isAllowedToBeEmbeddedInTrustedMode() {
+ return forAllActivities(this::isAllowedToEmbedActivityInTrustedMode);
+ }
+
+ /**
* Returns the TaskFragment that is being organized, which could be this or the ascendant
* TaskFragment.
*/
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 123ca88..19f921d 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -294,14 +294,28 @@
}
}
- /** Gets the {@link RemoteAnimationDefinition} set on the given organizer if exists. */
+ /**
+ * Gets the {@link RemoteAnimationDefinition} set on the given organizer if exists. Returns
+ * {@code null} if it doesn't, or if the organizer has activity(ies) embedded in untrusted mode.
+ */
@Nullable
public RemoteAnimationDefinition getRemoteAnimationDefinition(
ITaskFragmentOrganizer organizer) {
synchronized (mGlobalLock) {
final TaskFragmentOrganizerState organizerState =
mTaskFragmentOrganizerState.get(organizer.asBinder());
- return organizerState != null ? organizerState.mRemoteAnimationDefinition : null;
+ if (organizerState == null) {
+ return null;
+ }
+ for (TaskFragment tf : organizerState.mOrganizedTaskFragments) {
+ if (!tf.isAllowedToBeEmbeddedInTrustedMode()) {
+ // Disable client-driven animations for organizer if at least one of the
+ // embedded task fragments is not embedding in trusted mode.
+ // TODO(b/197364677): replace with a stub or Shell-driven one instead of skip?
+ return null;
+ }
+ }
+ return organizerState.mRemoteAnimationDefinition;
}
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 26871c3..8b8fd2c 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -825,7 +825,7 @@
// the bottom of the screen, so we need to animate it.
for (int i = 0; i < mTargets.size(); ++i) {
final Task task = mTargets.get(i).asTask();
- if (task == null || !task.isHomeOrRecentsRootTask()) continue;
+ if (task == null || !task.isActivityTypeHomeOrRecents()) continue;
animate = task.isVisibleRequested();
break;
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 073a508..5bfd546 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -80,6 +80,16 @@
}
@Override
+ public boolean getShowBackground() {
+ return mAnimation.getShowBackground();
+ }
+
+ @Override
+ public int getBackgroundColor() {
+ return mAnimation.getBackgroundColor();
+ }
+
+ @Override
public long getDuration() {
return mAnimation.computeDurationHint();
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 1bd305e..e1746cc 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -39,6 +39,7 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.AppTransition.MAX_APP_TRANSITION_DURATION;
+import static com.android.server.wm.AppTransition.isActivityTransitOld;
import static com.android.server.wm.AppTransition.isTaskTransitOld;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
@@ -99,6 +100,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.wm.SurfaceAnimator.Animatable;
@@ -2790,7 +2792,7 @@
@TransitionOldType int transit, boolean isVoiceInteraction,
@Nullable ArrayList<WindowContainer> sources) {
final Task task = asTask();
- if (task != null && !enter && !task.isHomeOrRecentsRootTask()) {
+ if (task != null && !enter && !task.isActivityTypeHomeOrRecents()) {
final InsetsControlTarget imeTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
final boolean isImeLayeringTarget = imeTarget != null && imeTarget.getWindow() != null
&& imeTarget.getWindow().getTask() == task;
@@ -2820,6 +2822,25 @@
}
}
+ final ActivityRecord activityRecord = asActivityRecord();
+ if (activityRecord != null && isActivityTransitOld(transit)
+ && adapter.getShowBackground()) {
+ final @ColorInt int backgroundColorForTransition;
+ if (adapter.getBackgroundColor() != 0) {
+ // If available use the background color provided through getBackgroundColor
+ // which if set originates from a call to overridePendingAppTransition.
+ backgroundColorForTransition = adapter.getBackgroundColor();
+ } else {
+ // Otherwise default to the window's background color if provided through
+ // the theme as the background color for the animation - the top most window
+ // with a valid background color and showBackground set takes precedence.
+ final Task arTask = activityRecord.getTask();
+ backgroundColorForTransition = ColorUtils.setAlphaComponent(
+ arTask.getTaskDescription().getBackgroundColor(), 255);
+ }
+ animationRunnerBuilder.setTaskBackgroundColor(backgroundColorForTransition);
+ }
+
animationRunnerBuilder.build()
.startAnimation(getPendingTransaction(), adapter, !isVisible(),
ANIMATION_TYPE_APP_TRANSITION, thumbnailAdapter);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 1d5c184..4c7891b 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -19,6 +19,8 @@
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.isStartResultSuccessful;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.WindowContainerTransaction.Change.CHANGE_BOUNDS_TRANSACTION;
+import static android.window.WindowContainerTransaction.Change.CHANGE_BOUNDS_TRANSACTION_RECT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT;
@@ -1224,10 +1226,14 @@
while (entries.hasNext()) {
final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
// Only allow to apply changes to TaskFragment that is created by this organizer.
- enforceTaskFragmentOrganized(func, WindowContainer.fromBinder(entry.getKey()),
- organizer);
+ WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
+ enforceTaskFragmentOrganized(func, wc, organizer);
+ enforceTaskFragmentConfigChangeAllowed(func, wc, entry.getValue(), organizer);
}
+ // TODO(b/197364677): Enforce safety of hierarchy operations in untrusted mode. E.g. one
+ // could first change a trusted TF, and then start/reparent untrusted activity there.
+
// Hierarchy changes
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
for (int i = hops.size() - 1; i >= 0; i--) {
@@ -1297,6 +1303,57 @@
}
}
+ /**
+ * Makes sure that SurfaceControl transactions and the ability to set bounds outside of the
+ * parent bounds are not allowed for embedding without full trust between the host and the
+ * target.
+ * TODO(b/197364677): Allow SC transactions when the client-driven animations are protected from
+ * tapjacking.
+ */
+ private void enforceTaskFragmentConfigChangeAllowed(String func, @Nullable WindowContainer wc,
+ WindowContainerTransaction.Change change, ITaskFragmentOrganizer organizer) {
+ if (wc == null) {
+ Slog.e(TAG, "Attempt to operate on task fragment that no longer exists");
+ return;
+ }
+ // Check if TaskFragment is embedded in fully trusted mode
+ if (wc.asTaskFragment().isAllowedToBeEmbeddedInTrustedMode()) {
+ // Fully trusted, no need to check further
+ return;
+ }
+
+ if (change == null) {
+ return;
+ }
+ final int changeMask = change.getChangeMask();
+ if ((changeMask & (CHANGE_BOUNDS_TRANSACTION | CHANGE_BOUNDS_TRANSACTION_RECT)) != 0) {
+ String msg = "Permission Denial: " + func + " from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + " trying to apply SurfaceControl changes to TaskFragment in non-trusted "
+ + "embedding mode, TaskFragmentOrganizer=" + organizer;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ if (change.getWindowSetMask() == 0) {
+ // Nothing else to check.
+ return;
+ }
+ WindowConfiguration requestedWindowConfig = change.getConfiguration().windowConfiguration;
+ WindowContainer wcParent = wc.getParent();
+ if (wcParent == null) {
+ Slog.e(TAG, "Attempt to set bounds on task fragment that has no parent");
+ return;
+ }
+ if (!wcParent.getBounds().contains(requestedWindowConfig.getBounds())) {
+ String msg = "Permission Denial: " + func + " from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + " trying to apply bounds outside of parent for non-trusted host,"
+ + " TaskFragmentOrganizer=" + organizer;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ }
+
void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
@Nullable IBinder errorCallbackToken, @NonNull CallerInfo caller) {
final ActivityRecord ownerActivity =
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index cc663d9..11300ce 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -508,7 +508,8 @@
AndroidPackage::shouldInheritKeyStoreKeys,
ParsingPackage::setInheritKeyStoreKeys,
true
- )
+ ),
+ getter(AndroidPackage::getKnownActivityEmbeddingCerts, setOf("TESTEMBEDDINGCERT"))
)
override fun initialObject() = PackageImpl.forParsing(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
index a89b717..5180786 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
@@ -53,7 +53,7 @@
ParsedActivity::getTaskAffinity,
ParsedActivity::getTheme,
ParsedActivity::getUiOptions,
- ParsedActivity::isSupportsSizeChanges,
+ ParsedActivity::isSupportsSizeChanges
)
override fun mainComponentSubclassExtraParams() = listOf(
@@ -73,6 +73,7 @@
ActivityInfo.WindowLayout::minHeight
)
}
- )
+ ),
+ getter(ParsedActivity::getKnownActivityEmbeddingCerts, setOf("TESTEMBEDDINGCERT"))
)
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index fb8749e..816dbdb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -23,6 +23,7 @@
import static android.app.ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED;
import static android.app.ActivityManager.RESTRICTION_LEVEL_EXEMPTED;
import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET;
+import static android.app.ActivityManager.isLowRamDeviceStatic;
import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_SYSTEM;
import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
@@ -46,6 +47,7 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.internal.notification.SystemNotificationChannels.ABUSIVE_BACKGROUND_APPS;
+import static com.android.server.am.AppBatteryTracker.AppBatteryPolicy.getFloatArray;
import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_INDEX_BACKGROUND;
import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND;
import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND_SERVICE;
@@ -108,6 +110,7 @@
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
import com.android.server.AppStateTracker;
import com.android.server.DeviceIdleInternal;
import com.android.server.am.AppBatteryExemptionTracker.AppBatteryExemptionPolicy;
@@ -554,28 +557,34 @@
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_MONITOR_ENABLED,
DeviceConfig::getBoolean,
- AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_MONITOR_ENABLED);
+ mContext.getResources().getBoolean(
+ R.bool.config_bg_current_drain_monitor_enabled));
bgCurrentDrainMonitor.set(true);
bgCurrentDrainWindow = new DeviceConfigSession<>(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_WINDOW,
DeviceConfig::getLong,
- AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_WINDOW_MS);
+ (long) mContext.getResources().getInteger(
+ R.integer.config_bg_current_drain_window));
bgCurrentDrainWindow.set(windowMs);
bgCurrentDrainRestrictedBucketThreshold = new DeviceConfigSession<>(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET,
DeviceConfig::getFloat,
- AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_THRESHOLD);
+ getFloatArray(mContext.getResources().obtainTypedArray(
+ R.array.config_bg_current_drain_threshold_to_restricted_bucket))[
+ isLowRamDeviceStatic() ? 1 : 0]);
bgCurrentDrainRestrictedBucketThreshold.set(restrictBucketThreshold);
bgCurrentDrainBgRestrictedThreshold = new DeviceConfigSession<>(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_BG_RESTRICTED,
DeviceConfig::getFloat,
- AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_THRESHOLD);
+ getFloatArray(mContext.getResources().obtainTypedArray(
+ R.array.config_bg_current_drain_threshold_to_bg_restricted))[
+ isLowRamDeviceStatic() ? 1 : 0]);
bgCurrentDrainBgRestrictedThreshold.set(bgRestrictedThreshold);
mCurrentTimeMillis = 10_000L;
@@ -1282,64 +1291,76 @@
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_MONITOR_ENABLED,
DeviceConfig::getBoolean,
- AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_MONITOR_ENABLED);
+ mContext.getResources().getBoolean(
+ R.bool.config_bg_current_drain_monitor_enabled));
bgCurrentDrainMonitor.set(true);
bgCurrentDrainWindow = new DeviceConfigSession<>(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_WINDOW,
DeviceConfig::getLong,
- AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_WINDOW_MS);
+ (long) mContext.getResources().getInteger(
+ R.integer.config_bg_current_drain_window));
bgCurrentDrainWindow.set(windowMs);
bgCurrentDrainRestrictedBucketThreshold = new DeviceConfigSession<>(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET,
DeviceConfig::getFloat,
- AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_THRESHOLD);
+ getFloatArray(mContext.getResources().obtainTypedArray(
+ R.array.config_bg_current_drain_threshold_to_restricted_bucket))[
+ isLowRamDeviceStatic() ? 1 : 0]);
bgCurrentDrainRestrictedBucketThreshold.set(restrictBucketThreshold);
bgCurrentDrainBgRestrictedThreshold = new DeviceConfigSession<>(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_BG_RESTRICTED,
DeviceConfig::getFloat,
- AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_THRESHOLD);
+ getFloatArray(mContext.getResources().obtainTypedArray(
+ R.array.config_bg_current_drain_threshold_to_bg_restricted))[
+ isLowRamDeviceStatic() ? 1 : 0]);
bgCurrentDrainBgRestrictedThreshold.set(bgRestrictedThreshold);
bgCurrentDrainRestrictedBucketHighThreshold = new DeviceConfigSession<>(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_RESTRICTED_BUCKET,
DeviceConfig::getFloat,
- AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_HIGH_THRESHOLD);
+ getFloatArray(mContext.getResources().obtainTypedArray(
+ R.array.config_bg_current_drain_high_threshold_to_restricted_bucket))[
+ isLowRamDeviceStatic() ? 1 : 0]);
bgCurrentDrainRestrictedBucketHighThreshold.set(restrictBucketHighThreshold);
bgCurrentDrainBgRestrictedHighThreshold = new DeviceConfigSession<>(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_BG_RESTRICTED,
DeviceConfig::getFloat,
- AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_HIGH_THRESHOLD);
+ getFloatArray(mContext.getResources().obtainTypedArray(
+ R.array.config_bg_current_drain_high_threshold_to_bg_restricted))[
+ isLowRamDeviceStatic() ? 1 : 0]);
bgCurrentDrainBgRestrictedHighThreshold.set(bgRestrictedHighThreshold);
bgMediaPlaybackMinDurationThreshold = new DeviceConfigSession<>(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION,
DeviceConfig::getLong,
- AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION);
+ (long) mContext.getResources().getInteger(
+ R.integer.config_bg_current_drain_media_playback_min_duration));
bgMediaPlaybackMinDurationThreshold.set(bgMediaPlaybackMinDuration);
bgLocationMinDurationThreshold = new DeviceConfigSession<>(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_LOCATION_MIN_DURATION,
DeviceConfig::getLong,
- AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_LOCATION_MIN_DURATION);
+ (long) mContext.getResources().getInteger(
+ R.integer.config_bg_current_drain_location_min_duration));
bgLocationMinDurationThreshold.set(bgLocationMinDuration);
bgCurrentDrainEventDurationBasedThresholdEnabled = new DeviceConfigSession<>(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_EVENT_DURATION_BASED_THRESHOLD_ENABLED,
DeviceConfig::getBoolean,
- AppBatteryPolicy
- .DEFAULT_BG_CURRENT_DRAIN_EVENT_DURATION_BASED_THRESHOLD_ENABLED);
+ mContext.getResources().getBoolean(
+ R.bool.config_bg_current_drain_event_duration_based_threshold_enabled));
bgCurrentDrainEventDurationBasedThresholdEnabled.set(true);
bgBatteryExemptionEnabled = new DeviceConfigSession<>(
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index 0b488b2..5fb3a4e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -33,6 +33,7 @@
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -445,6 +446,48 @@
mService.getWallpaperDimAmount(), dimAmount, 0.0);
}
+ @Test
+ public void testGetAdjustedWallpaperColorsOnDimming() throws RemoteException {
+ final int testUserId = USER_SYSTEM;
+ mService.switchUser(testUserId, null);
+ mService.setWallpaperComponent(sDefaultWallpaperComponent);
+ WallpaperData wallpaper = mService.getCurrentWallpaperData(FLAG_SYSTEM, testUserId);
+
+ // Mock a wallpaper data with color hints that support dark text and dark theme
+ // but not HINT_FROM_BITMAP
+ wallpaper.primaryColors = new WallpaperColors(Color.valueOf(Color.WHITE), null, null,
+ WallpaperColors.HINT_SUPPORTS_DARK_TEXT | WallpaperColors.HINT_SUPPORTS_DARK_THEME);
+ mService.setWallpaperDimAmount(0.6f);
+ int colorHints = mService.getAdjustedWallpaperColorsOnDimming(wallpaper).getColorHints();
+ // Dimmed wallpaper not extracted from bitmap does not support dark text and dark theme
+ assertNotEquals(WallpaperColors.HINT_SUPPORTS_DARK_TEXT,
+ colorHints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT);
+ assertNotEquals(WallpaperColors.HINT_SUPPORTS_DARK_THEME,
+ colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME);
+
+ // Remove dimming
+ mService.setWallpaperDimAmount(0f);
+ colorHints = mService.getAdjustedWallpaperColorsOnDimming(wallpaper).getColorHints();
+ // Undimmed wallpaper not extracted from bitmap does support dark text and dark theme
+ assertEquals(WallpaperColors.HINT_SUPPORTS_DARK_TEXT,
+ colorHints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT);
+ assertEquals(WallpaperColors.HINT_SUPPORTS_DARK_THEME,
+ colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME);
+
+ // Mock a wallpaper data with color hints that support dark text and dark theme
+ // and was extracted from bitmap
+ wallpaper.primaryColors = new WallpaperColors(Color.valueOf(Color.WHITE), null, null,
+ WallpaperColors.HINT_SUPPORTS_DARK_TEXT | WallpaperColors.HINT_SUPPORTS_DARK_THEME
+ | WallpaperColors.HINT_FROM_BITMAP);
+ mService.setWallpaperDimAmount(0.6f);
+ colorHints = mService.getAdjustedWallpaperColorsOnDimming(wallpaper).getColorHints();
+ // Dimmed wallpaper should still support dark text and dark theme
+ assertEquals(WallpaperColors.HINT_SUPPORTS_DARK_TEXT,
+ colorHints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT);
+ assertEquals(WallpaperColors.HINT_SUPPORTS_DARK_THEME,
+ colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME);
+ }
+
// Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for
// non-current user must not bind to wallpaper service.
private void verifyNoConnectionBeforeLastUser(int lastUserId) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 31bdec1..952200d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -895,6 +895,7 @@
assertEquals(a.networkSecurityConfigRes, that.networkSecurityConfigRes);
assertEquals(a.taskAffinity, that.taskAffinity);
assertEquals(a.permission, that.permission);
+ assertEquals(a.getKnownActivityEmbeddingCerts(), that.getKnownActivityEmbeddingCerts());
assertEquals(a.processName, that.processName);
assertEquals(a.className, that.className);
assertEquals(a.manageSpaceActivityName, that.manageSpaceActivityName);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ef9494a..dfcab2b 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -102,6 +102,7 @@
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
+import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
@@ -241,6 +242,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
@RunWithLooper
public class NotificationManagerServiceTest extends UiServiceTestCase {
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
@@ -1338,7 +1340,8 @@
mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
NotificationManagerService.PostNotificationRunnable runnable =
- mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+ mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+ r.getUid(), SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
@@ -1359,7 +1362,8 @@
when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(IMPORTANCE_NONE);
NotificationManagerService.PostNotificationRunnable runnable =
- mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+ mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+ r.getUid(), SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
@@ -3921,7 +3925,8 @@
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, null, false);
mService.addEnqueuedNotification(r);
NotificationManagerService.PostNotificationRunnable runnable =
- mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+ mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+ r.getUid(), SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
@@ -3938,7 +3943,8 @@
r = generateNotificationRecord(mTestNotificationChannel, 0, null, false);
mService.addEnqueuedNotification(r);
NotificationManagerService.PostNotificationRunnable runnable =
- mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+ mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+ r.getUid(), SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
@@ -3954,7 +3960,8 @@
mService.addEnqueuedNotification(r);
NotificationManagerService.PostNotificationRunnable runnable =
- mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+ mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+ r.getUid(), SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
@@ -3967,12 +3974,14 @@
r.setCriticality(CriticalNotificationExtractor.CRITICAL_LOW);
mService.addEnqueuedNotification(r);
NotificationManagerService.PostNotificationRunnable runnable =
- mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+ mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+ r.getUid(), SystemClock.elapsedRealtime());
runnable.run();
r = generateNotificationRecord(mTestNotificationChannel, 1, null, false);
r.setCriticality(CriticalNotificationExtractor.CRITICAL);
- runnable = mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+ runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+ r.getUid(), SystemClock.elapsedRealtime());
mService.addEnqueuedNotification(r);
runnable.run();
@@ -4006,6 +4015,63 @@
}
@Test
+ public void testMediaStyleRemote_hasPermission() throws RemoteException {
+ String deviceName = "device";
+ when(mPackageManager.checkPermission(
+ eq(android.Manifest.permission.MEDIA_CONTENT_CONTROL), any(), anyInt()))
+ .thenReturn(PERMISSION_GRANTED);
+ Notification.MediaStyle style = new Notification.MediaStyle();
+ style.setRemotePlaybackInfo(deviceName, 0, null);
+ Notification.Builder nb = new Notification.Builder(mContext,
+ mTestNotificationChannel.getId())
+ .setStyle(style);
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+ "testMediaStyleRemoteHasPermission", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+ waitForIdle();
+
+ NotificationRecord posted = mService.findNotificationLocked(
+ PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId());
+ Bundle extras = posted.getNotification().extras;
+
+ assertTrue(extras.containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE));
+ assertEquals(deviceName, extras.getString(Notification.EXTRA_MEDIA_REMOTE_DEVICE));
+ }
+
+ @Test
+ public void testMediaStyleRemote_noPermission() throws RemoteException {
+ String deviceName = "device";
+ when(mPackageManager.checkPermission(
+ eq(android.Manifest.permission.MEDIA_CONTENT_CONTROL), any(), anyInt()))
+ .thenReturn(PERMISSION_DENIED);
+ Notification.MediaStyle style = new Notification.MediaStyle();
+ style.setRemotePlaybackInfo(deviceName, 0, null);
+ Notification.Builder nb = new Notification.Builder(mContext,
+ mTestNotificationChannel.getId())
+ .setStyle(style);
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+ "testMediaStyleRemoteNoPermission", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+ waitForIdle();
+
+ NotificationRecord posted = mService.findNotificationLocked(
+ PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId());
+
+ assertFalse(posted.getNotification().extras
+ .containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE));
+ }
+
+ @Test
public void testGetNotificationCountLocked() {
String sampleTagToExclude = null;
int sampleIdToExclude = 0;
@@ -4416,6 +4482,8 @@
NotificationManagerService.PostNotificationRunnable runnable =
mService.new PostNotificationRunnable(original.getKey(),
+ original.getSbn().getPackageName(),
+ original.getUid(),
SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
@@ -4438,6 +4506,8 @@
NotificationManagerService.PostNotificationRunnable runnable =
mService.new PostNotificationRunnable(update.getKey(),
+ update.getSbn().getPackageName(),
+ update.getUid(),
SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
@@ -6533,7 +6603,8 @@
assertNull(update.getSbn().getNotification().getSmallIcon());
NotificationManagerService.PostNotificationRunnable runnable =
- mService.new PostNotificationRunnable(update.getKey(),
+ mService.new PostNotificationRunnable(update.getKey(), r.getSbn().getPackageName(),
+ r.getUid(),
SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index fec5405..d922f40 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -657,7 +657,8 @@
when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false);
NotificationManagerService.PostNotificationRunnable runnable =
- mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+ mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+ r.getUid(), SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
@@ -790,7 +791,8 @@
mService.addEnqueuedNotification(r);
NotificationManagerService.PostNotificationRunnable runnable =
- mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+ mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+ r.getUid(), SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
@@ -806,7 +808,8 @@
r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mService.addEnqueuedNotification(r);
- runnable = mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+ runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+ r.getUid(), SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
@@ -822,7 +825,8 @@
r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mService.addEnqueuedNotification(r);
- runnable = mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+ runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+ r.getUid(), SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index c58bf3b..c8e48a4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -75,6 +75,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.SigningDetails;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
@@ -84,9 +85,11 @@
import android.service.voice.IVoiceInteractionSession;
import android.util.Pair;
import android.view.Gravity;
+import android.window.TaskFragmentOrganizerToken;
import androidx.test.filters.SmallTest;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
import com.android.server.wm.utils.MockTracker;
@@ -94,6 +97,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
/**
* Tests for the {@link ActivityStarter} class.
*
@@ -1109,7 +1116,7 @@
}
@Test
- public void testStartActivityInner_inTaskFragment() {
+ public void testStartActivityInner_inTaskFragment_failsByDefault() {
final ActivityStarter starter = prepareStarter(0, false);
final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
@@ -1130,6 +1137,97 @@
/* restrictedBgActivity */false,
/* intentGrants */null);
+ assertFalse(taskFragment.hasChild());
+ }
+
+ @Test
+ public void testStartActivityInner_inTaskFragment_allowedForSameUid() {
+ final ActivityStarter starter = prepareStarter(0, false);
+ final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
+ final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final TaskFragment taskFragment = new TaskFragment(mAtm, sourceRecord.token,
+ true /* createdByOrganizer */);
+ sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
+
+ taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class),
+ targetRecord.getUid(), "test_process_name");
+
+ starter.startActivityInner(
+ /* r */targetRecord,
+ /* sourceRecord */ sourceRecord,
+ /* voiceSession */null,
+ /* voiceInteractor */ null,
+ /* startFlags */ 0,
+ /* doResume */true,
+ /* options */null,
+ /* inTask */null,
+ /* inTaskFragment */ taskFragment,
+ /* restrictedBgActivity */false,
+ /* intentGrants */null);
+
+ assertTrue(taskFragment.hasChild());
+ }
+
+ @Test
+ public void testStartActivityInner_inTaskFragment_allowedTrustedCertUid() {
+ final ActivityStarter starter = prepareStarter(0, false);
+ final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
+ final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final TaskFragment taskFragment = new TaskFragment(mAtm, sourceRecord.token,
+ true /* createdByOrganizer */);
+ sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
+
+ taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class),
+ 12345, "test_process_name");
+ AndroidPackage androidPackage = mock(AndroidPackage.class);
+ doReturn(androidPackage).when(mMockPackageManager).getPackage(eq(12345));
+
+ Set<String> certs = new HashSet(Arrays.asList("test_cert1", "test_cert1"));
+ targetRecord.info.setKnownActivityEmbeddingCerts(certs);
+ SigningDetails signingDetails = mock(SigningDetails.class);
+ doReturn(true).when(signingDetails).hasAncestorOrSelfWithDigest(any());
+ doReturn(signingDetails).when(androidPackage).getSigningDetails();
+
+ starter.startActivityInner(
+ /* r */targetRecord,
+ /* sourceRecord */ sourceRecord,
+ /* voiceSession */null,
+ /* voiceInteractor */ null,
+ /* startFlags */ 0,
+ /* doResume */true,
+ /* options */null,
+ /* inTask */null,
+ /* inTaskFragment */ taskFragment,
+ /* restrictedBgActivity */false,
+ /* intentGrants */null);
+
+ assertTrue(taskFragment.hasChild());
+ }
+
+ @Test
+ public void testStartActivityInner_inTaskFragment_allowedForUntrustedEmbedding() {
+ final ActivityStarter starter = prepareStarter(0, false);
+ final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
+ final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final TaskFragment taskFragment = new TaskFragment(mAtm, sourceRecord.token,
+ true /* createdByOrganizer */);
+ sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
+
+ targetRecord.info.flags |= ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
+
+ starter.startActivityInner(
+ /* r */targetRecord,
+ /* sourceRecord */ sourceRecord,
+ /* voiceSession */null,
+ /* voiceInteractor */ null,
+ /* startFlags */ 0,
+ /* doResume */true,
+ /* options */null,
+ /* inTask */null,
+ /* inTaskFragment */ taskFragment,
+ /* restrictedBgActivity */false,
+ /* intentGrants */null);
+
assertTrue(taskFragment.hasChild());
}
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index 30ed7c2..8d92520 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -524,7 +524,10 @@
* defined in {@code RESOLVABLE_ERROR_}. A subclass should override this method. Otherwise,
* this method does nothing and returns null by default.
* @see android.telephony.euicc.EuiccManager#downloadSubscription
+ * @deprecated prefer {@link #onDownloadSubscription(int, int,
+ * DownloadableSubscription, boolean, boolean, Bundle)}
*/
+ @Deprecated
public DownloadSubscriptionResult onDownloadSubscription(int slotId,
@NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
boolean forceDeactivateSim, @Nullable Bundle resolvedBundle) {
@@ -534,6 +537,37 @@
/**
* Download the given subscription.
*
+ * @param slotIndex Index of the SIM slot to use for the operation.
+ * @param portIndex Index of the port from the slot. portIndex is used when
+ * switchAfterDownload is set to {@code true}, otherwise download is port agnostic.
+ * @param subscription The subscription to download.
+ * @param switchAfterDownload If true, the subscription should be enabled upon successful
+ * download.
+ * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
+ * eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM}
+ * should be returned to allow the user to consent to this operation first.
+ * @param resolvedBundle The bundle containing information on resolved errors. It can contain
+ * a string of confirmation code for the key {@link #EXTRA_RESOLUTION_CONFIRMATION_CODE},
+ * and a boolean for key {@link #EXTRA_RESOLUTION_ALLOW_POLICY_RULES} indicating whether
+ * the user allows profile policy rules or not.
+ * @return a DownloadSubscriptionResult instance including a result code, a resolvable errors
+ * bit map, and original the card Id. The result code may be one of the predefined
+ * {@code RESULT_} constants or any implementation-specific code starting with
+ * {@link #RESULT_FIRST_USER}. The resolvable error bit map can be either 0 or values
+ * defined in {@code RESOLVABLE_ERROR_}.
+ * @see android.telephony.euicc.EuiccManager#downloadSubscription
+ */
+ @NonNull
+ public DownloadSubscriptionResult onDownloadSubscription(int slotIndex, int portIndex,
+ @NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
+ boolean forceDeactivateSim, @NonNull Bundle resolvedBundle) {
+ // stub implementation, LPA needs to implement this
+ throw new UnsupportedOperationException("LPA must override onDownloadSubscription");
+ }
+
+ /**
+ * Download the given subscription.
+ *
* @param slotId ID of the SIM slot to use for the operation.
* @param subscription The subscription to download.
* @param switchAfterDownload If true, the subscription should be enabled upon successful
@@ -701,7 +735,8 @@
*/
private class IEuiccServiceWrapper extends IEuiccService.Stub {
@Override
- public void downloadSubscription(int slotId, DownloadableSubscription subscription,
+ public void downloadSubscription(int slotId, int portIndex,
+ DownloadableSubscription subscription,
boolean switchAfterDownload, boolean forceDeactivateSim, Bundle resolvedBundle,
IDownloadSubscriptionCallback callback) {
mExecutor.execute(new Runnable() {
diff --git a/telephony/java/android/service/euicc/IEuiccService.aidl b/telephony/java/android/service/euicc/IEuiccService.aidl
index 030e11a..6b0397d 100644
--- a/telephony/java/android/service/euicc/IEuiccService.aidl
+++ b/telephony/java/android/service/euicc/IEuiccService.aidl
@@ -35,9 +35,9 @@
/** @hide */
oneway interface IEuiccService {
- void downloadSubscription(int slotId, in DownloadableSubscription subscription,
- boolean switchAfterDownload, boolean forceDeactivateSim, in Bundle resolvedBundle,
- in IDownloadSubscriptionCallback callback);
+ void downloadSubscription(int slotId, int portIndex, in DownloadableSubscription subscription,
+ boolean switchAfterDownload, boolean forceDeactivateSim, in Bundle resolvedBundle,
+ in IDownloadSubscriptionCallback callback);
void getDownloadableSubscriptionMetadata(int slotId, in DownloadableSubscription subscription,
boolean forceDeactivateSim, in IGetDownloadableSubscriptionMetadataCallback callback);
void getEid(int slotId, in IGetEidCallback callback);
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index 1ec83a3..b18bdff 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -69,7 +69,6 @@
"test/**/*.proto",
],
proto: {
- plugin: "javastream",
+ type: "stream",
},
- static_libs: ["libprotobuf-java-lite"],
}
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index d3eb8e0..9604475 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -1269,18 +1269,19 @@
* support the NL80211_CMD_REG_CHANGED (otherwise it will find out on its own). The wificond
* updates in internal state in response to this Country Code update.
*
- * @return true on success, false otherwise.
+ * @param newCountryCode new country code. An ISO-3166-alpha2 country code which is 2-Character
+ * alphanumeric.
*/
- public boolean notifyCountryCodeChanged() {
- try {
- if (mWificond != null) {
- mWificond.notifyCountryCodeChanged();
- return true;
- }
- } catch (RemoteException e1) {
- Log.e(TAG, "Failed to notify country code changed due to remote exception");
+ public void notifyCountryCodeChanged(@Nullable String newCountryCode) {
+ if (mWificond == null) {
+ new RemoteException("Wificond service doesn't exist!").rethrowFromSystemServer();
}
- return false;
+ try {
+ mWificond.notifyCountryCodeChanged();
+ Log.i(TAG, "Receive country code change to " + newCountryCode);
+ } catch (RemoteException re) {
+ re.rethrowFromSystemServer();
+ }
}
/**
diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
index 4032a7b..a750696 100644
--- a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
@@ -1143,17 +1143,17 @@
@Test
public void testNotifyCountryCodeChanged() throws Exception {
doNothing().when(mWificond).notifyCountryCodeChanged();
- assertTrue(mWificondControl.notifyCountryCodeChanged());
+ mWificondControl.notifyCountryCodeChanged(TEST_COUNTRY_CODE);
verify(mWificond).notifyCountryCodeChanged();
}
/**
* Tests notifyCountryCodeChanged with RemoteException
*/
- @Test
+ @Test(expected = RuntimeException.class)
public void testNotifyCountryCodeChangedRemoteException() throws Exception {
doThrow(new RemoteException()).when(mWificond).notifyCountryCodeChanged();
- assertFalse(mWificondControl.notifyCountryCodeChanged());
+ mWificondControl.notifyCountryCodeChanged(TEST_COUNTRY_CODE);
verify(mWificond).notifyCountryCodeChanged();
}