Merge "Add NonNull annotaion for LineHeightSpan.Standard"
diff --git a/api/current.txt b/api/current.txt
index 57a43ef..4e533d1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -16797,6 +16797,7 @@
public abstract static class CameraManager.AvailabilityCallback {
ctor public CameraManager.AvailabilityCallback();
+ method public void onCameraAccessPrioritiesChanged();
method public void onCameraAvailable(@NonNull String);
method public void onCameraUnavailable(@NonNull String);
}
@@ -22857,7 +22858,6 @@
method public float getBearing();
method public float getBearingAccuracyDegrees();
method public long getElapsedRealtimeNanos();
- method public long getElapsedRealtimeUncertaintyNanos();
method public android.os.Bundle getExtras();
method public double getLatitude();
method public double getLongitude();
@@ -22870,7 +22870,6 @@
method public boolean hasAltitude();
method public boolean hasBearing();
method public boolean hasBearingAccuracy();
- method public boolean hasElapsedRealtimeUncertaintyNanos();
method public boolean hasSpeed();
method public boolean hasSpeedAccuracy();
method public boolean hasVerticalAccuracy();
@@ -22886,7 +22885,6 @@
method public void setBearing(float);
method public void setBearingAccuracyDegrees(float);
method public void setElapsedRealtimeNanos(long);
- method public void setElapsedRealtimeUncertaintyNanos(long);
method public void setExtras(android.os.Bundle);
method public void setLatitude(double);
method public void setLongitude(double);
@@ -23570,7 +23568,6 @@
method @Deprecated public void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
method public int attachAuxEffect(int);
method @NonNull public android.media.VolumeShaper createVolumeShaper(@NonNull android.media.VolumeShaper.Configuration);
- method @Deprecated public static void deprecateStreamTypeForPlayback(int, @NonNull String, @NonNull String) throws java.lang.IllegalArgumentException;
method protected void finalize();
method public void flush();
method @NonNull public android.media.AudioAttributes getAudioAttributes();
@@ -25327,7 +25324,6 @@
method public static android.media.MediaPlayer create(android.content.Context, int);
method public static android.media.MediaPlayer create(android.content.Context, int, android.media.AudioAttributes, int);
method @NonNull public android.media.VolumeShaper createVolumeShaper(@NonNull android.media.VolumeShaper.Configuration);
- method @Deprecated public static void deprecateStreamTypeForPlayback(int, @NonNull String, @NonNull String) throws java.lang.IllegalArgumentException;
method public void deselectTrack(int) throws java.lang.IllegalStateException;
method protected void finalize();
method public int getAudioSessionId();
@@ -26422,7 +26418,6 @@
ctor @Deprecated public SoundPool(int, int, int);
method public final void autoPause();
method public final void autoResume();
- method @Deprecated public static void deprecateStreamTypeForPlayback(int, @NonNull String, @NonNull String) throws java.lang.IllegalArgumentException;
method protected void finalize();
method public int load(String, int);
method public int load(android.content.Context, int, int);
@@ -45147,8 +45142,8 @@
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getImei();
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getImei(int);
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}) public String getLine1Number();
- method public String getManufacturerCode();
- method public String getManufacturerCode(int);
+ method @Nullable public String getManufacturerCode();
+ method @Nullable public String getManufacturerCode(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getMeid();
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getMeid(int);
method public String getMmsUAProfUrl();
@@ -45175,8 +45170,8 @@
method public int getSimState();
method public int getSimState(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getSubscriberId();
- method public String getTypeAllocationCode();
- method public String getTypeAllocationCode(int);
+ method @Nullable public String getTypeAllocationCode();
+ method @Nullable public String getTypeAllocationCode(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @NonNull public java.util.List<android.telephony.UiccCardInfo> getUiccCardsInfo();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVisualVoicemailPackageName();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailAlphaTag();
@@ -45562,7 +45557,7 @@
}
public class EuiccManager {
- method public android.telephony.euicc.EuiccManager createForCardId(int);
+ method @NonNull public android.telephony.euicc.EuiccManager createForCardId(int);
method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void deleteSubscription(int, android.app.PendingIntent);
method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void downloadSubscription(android.telephony.euicc.DownloadableSubscription, boolean, android.app.PendingIntent);
method @Nullable public String getEid();
diff --git a/api/system-current.txt b/api/system-current.txt
index 418dfb1..752640b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -181,6 +181,7 @@
field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
+ field public static final String SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON = "android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
field public static final String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE";
@@ -1187,7 +1188,7 @@
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getAppStandbyBucket(String);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public java.util.Map<java.lang.String,java.lang.Integer> getAppStandbyBuckets();
method public int getUsageSource();
- method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @Nullable android.app.PendingIntent);
+ method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], @NonNull java.time.Duration, @NonNull java.time.Duration, @Nullable android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerAppUsageObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerUsageSessionObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent, @Nullable android.app.PendingIntent);
method public void reportUsageStart(@NonNull android.app.Activity, @NonNull String);
@@ -3540,7 +3541,7 @@
method public void stop();
}
- public static class HwAudioSource.Builder {
+ public static final class HwAudioSource.Builder {
ctor public HwAudioSource.Builder();
method @NonNull public android.media.HwAudioSource build();
method @NonNull public android.media.HwAudioSource.Builder setAudioAttributes(@NonNull android.media.AudioAttributes);
@@ -3678,7 +3679,7 @@
method @Nullable public android.media.audiopolicy.AudioProductStrategy getProductStrategyForAudioAttributes(@NonNull android.media.AudioAttributes);
method public int getVolumeGroupIdForAttributes(@NonNull android.media.AudioAttributes);
method public int getVolumeGroupIdForLegacyStreamType(int);
- method public java.util.Iterator<android.media.audiopolicy.AudioProductStrategy> iterator();
+ method @NonNull public java.util.Iterator<android.media.audiopolicy.AudioProductStrategy> iterator();
method public int size();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioProductStrategies> CREATOR;
@@ -3695,7 +3696,7 @@
public final class AudioVolumeGroup implements android.os.Parcelable {
method public int describeContents();
- method public java.util.List<android.media.AudioAttributes> getAudioAttributes();
+ method @NonNull public java.util.List<android.media.AudioAttributes> getAudioAttributes();
method public int getId();
method @NonNull public int[] getLegacyStreamTypes();
method @NonNull public String name();
@@ -3707,7 +3708,7 @@
ctor public AudioVolumeGroups();
method public int describeContents();
method @Nullable public android.media.audiopolicy.AudioVolumeGroup getById(int);
- method public java.util.Iterator<android.media.audiopolicy.AudioVolumeGroup> iterator();
+ method @NonNull public java.util.Iterator<android.media.audiopolicy.AudioVolumeGroup> iterator();
method public int size();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioVolumeGroups> CREATOR;
@@ -5688,6 +5689,7 @@
method @NonNull public abstract java.util.List<android.permission.RuntimePermissionUsageInfo> onGetPermissionUsages(boolean, long);
method public abstract void onGetRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.OutputStream);
method public abstract boolean onIsApplicationQualifiedForRole(@NonNull String, @NonNull String);
+ method public abstract boolean onIsRoleVisible(@NonNull String);
method @BinderThread public abstract boolean onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle);
method @BinderThread public abstract void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream);
method public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String);
@@ -5851,19 +5853,17 @@
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void resetToDefaults(int, @Nullable String);
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(String, String, String, boolean);
field public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager";
+ field public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT = "activity_manager_native_boot";
field public static final String NAMESPACE_AUTOFILL = "autofill";
field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
field public static final String NAMESPACE_GAME_DRIVER = "game_driver";
field public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
+ field public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
field public static final String NAMESPACE_NETD_NATIVE = "netd_native";
+ field public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
field public static final String NAMESPACE_SYSTEMUI = "systemui";
}
- public static interface DeviceConfig.ActivityManagerNativeBoot {
- field public static final String NAMESPACE = "activity_manager_native_boot";
- field public static final String OFFLOAD_QUEUE_ENABLED = "offload_queue_enabled";
- }
-
public static interface DeviceConfig.AttentionManagerService {
field public static final String COMPONENT_NAME = "component_name";
field public static final String NAMESPACE = "attention_manager_service";
@@ -5882,10 +5882,6 @@
field public static final String NAMESPACE = "intelligence_attention";
}
- public static interface DeviceConfig.MediaNative {
- field public static final String NAMESPACE = "media_native";
- }
-
public static interface DeviceConfig.OnPropertyChangedListener {
method public void onPropertyChanged(String, String, String);
}
@@ -5912,10 +5908,6 @@
field public static final String NAMESPACE = "runtime_native";
}
- public static interface DeviceConfig.RuntimeNativeBoot {
- field public static final String NAMESPACE = "runtime_native_boot";
- }
-
public static interface DeviceConfig.Scheduler {
field public static final String ENABLE_FAST_METRICS_COLLECTION = "enable_fast_metrics_collection";
field public static final String NAMESPACE = "scheduler";
@@ -6433,8 +6425,8 @@
public abstract class ApnService extends android.app.Service {
ctor public ApnService();
- method public android.os.IBinder onBind(android.content.Intent);
- method @WorkerThread public abstract java.util.List<android.content.ContentValues> onRestoreApns(int);
+ method @NonNull public android.os.IBinder onBind(@Nullable android.content.Intent);
+ method @WorkerThread @NonNull public abstract java.util.List<android.content.ContentValues> onRestoreApns(int);
}
}
@@ -7106,12 +7098,12 @@
field public static final int WWAN = 1; // 0x1
}
- public class CallAttributes implements android.os.Parcelable {
- ctor public CallAttributes(android.telephony.PreciseCallState, int, android.telephony.CallQuality);
+ public final class CallAttributes implements android.os.Parcelable {
+ ctor public CallAttributes(@NonNull android.telephony.PreciseCallState, int, @NonNull android.telephony.CallQuality);
method public int describeContents();
- method public android.telephony.CallQuality getCallQuality();
+ method @NonNull public android.telephony.CallQuality getCallQuality();
method public int getNetworkType();
- method public android.telephony.PreciseCallState getPreciseCallState();
+ method @NonNull public android.telephony.PreciseCallState getPreciseCallState();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR;
}
@@ -7959,7 +7951,7 @@
method @Deprecated public boolean getDataEnabled(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.util.Pair<java.lang.Integer,java.lang.Integer>> getLogicalToPhysicalSlotMapping();
method public static long getMaxNumberVerificationTimeoutMillis();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmap();
@@ -8435,7 +8427,7 @@
}
public class ImsCallSessionListener {
- method public void callQualityChanged(android.telephony.CallQuality);
+ method public void callQualityChanged(@NonNull android.telephony.CallQuality);
method public void callSessionConferenceExtendFailed(android.telephony.ims.ImsReasonInfo);
method public void callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
method public void callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
@@ -8460,7 +8452,7 @@
method public void callSessionResumeFailed(android.telephony.ims.ImsReasonInfo);
method public void callSessionResumeReceived(android.telephony.ims.ImsCallProfile);
method public void callSessionResumed(android.telephony.ims.ImsCallProfile);
- method public void callSessionRttAudioIndicatorChanged(android.telephony.ims.ImsStreamMediaProfile);
+ method public void callSessionRttAudioIndicatorChanged(@NonNull android.telephony.ims.ImsStreamMediaProfile);
method public void callSessionRttMessageReceived(String);
method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile);
method public void callSessionRttModifyResponseReceived(int);
@@ -8764,12 +8756,12 @@
public final class ImsSsData implements android.os.Parcelable {
ctor public ImsSsData(int, int, int, int, int);
method public int describeContents();
- method public android.telephony.ims.ImsCallForwardInfo[] getCallForwardInfo();
+ method @Nullable public java.util.List<android.telephony.ims.ImsCallForwardInfo> getCallForwardInfo();
method public int getRequestType();
method public int getResult();
method public int getServiceClass();
method public int getServiceType();
- method @NonNull public android.telephony.ims.ImsSsInfo[] getSuppServiceInfo();
+ method @NonNull public java.util.List<android.telephony.ims.ImsSsInfo> getSuppServiceInfo();
method public int getTeleserviceType();
method public boolean isTypeBarring();
method public boolean isTypeCf();
@@ -8829,11 +8821,11 @@
field public static final int SS_WAIT = 12; // 0xc
}
- public static class ImsSsData.Builder {
+ public static final class ImsSsData.Builder {
ctor public ImsSsData.Builder(int, int, int, int, int);
method @NonNull public android.telephony.ims.ImsSsData build();
- method @NonNull public android.telephony.ims.ImsSsData.Builder setCallForwardingInfo(@NonNull android.telephony.ims.ImsCallForwardInfo[]);
- method @NonNull public android.telephony.ims.ImsSsData.Builder setSuppServiceInfo(@NonNull android.telephony.ims.ImsSsInfo[]);
+ method @NonNull public android.telephony.ims.ImsSsData.Builder setCallForwardingInfo(@NonNull java.util.List<android.telephony.ims.ImsCallForwardInfo>);
+ method @NonNull public android.telephony.ims.ImsSsData.Builder setSuppServiceInfo(@NonNull java.util.List<android.telephony.ims.ImsSsInfo>);
}
public final class ImsSsInfo implements android.os.Parcelable {
@@ -8842,7 +8834,7 @@
method public int getClirInterrogationStatus();
method public int getClirOutgoingState();
method @Deprecated public String getIcbNum();
- method public String getIncomingCommunicationBarringNumber();
+ method @Nullable public String getIncomingCommunicationBarringNumber();
method public int getProvisionStatus();
method public int getStatus();
method public void writeToParcel(android.os.Parcel, int);
@@ -8863,7 +8855,7 @@
field public static final int SERVICE_PROVISIONING_UNKNOWN = -1; // 0xffffffff
}
- public static class ImsSsInfo.Builder {
+ public static final class ImsSsInfo.Builder {
ctor public ImsSsInfo.Builder(int);
method @NonNull public android.telephony.ims.ImsSsInfo build();
method @NonNull public android.telephony.ims.ImsSsInfo.Builder setClirInterrogationStatus(int);
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index b72ec39..62b24e9 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -60,7 +60,7 @@
in PendingIntent sessionEndCallbackIntent, String callingPackage);
void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage);
void registerAppUsageLimitObserver(int observerId, in String[] packages, long timeLimitMs,
- in PendingIntent callback, String callingPackage);
+ long timeRemainingMs, in PendingIntent callback, String callingPackage);
void unregisterAppUsageLimitObserver(int observerId, String callingPackage);
void reportUsageStart(in IBinder activity, String token, String callingPackage);
void reportPastUsageStart(in IBinder activity, String token, long timeAgoMs,
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index d34e6d3..cee6b87 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -35,6 +35,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -738,6 +739,23 @@
}
/**
+ * @deprecated use
+ * {@link #registerAppUsageLimitObserver(int, String[], Duration, Duration, PendingIntent)}.
+ *
+ * @removed
+ * @hide
+ */
+ @Deprecated
+ @UnsupportedAppUsage
+ // STOPSHIP b/126917290: remove this method once ag/6591106 is merged and it's not being used.
+ public void registerAppUsageLimitObserver(int observerId, @NonNull String[] observedEntities,
+ long timeLimit, @NonNull TimeUnit timeUnit, @Nullable PendingIntent callbackIntent) {
+ final Duration timeLimitDuration = Duration.ofMillis(timeUnit.toMillis(timeLimit));
+ registerAppUsageLimitObserver(observerId, observedEntities,
+ timeLimitDuration, timeLimitDuration, callbackIntent);
+ }
+
+ /**
* Register a usage limit observer that receives a callback on the provided intent when the
* sum of usages of apps and tokens in the provided {@code observedEntities} array exceeds the
* {@code timeLimit} specified. The structure of a token is a {@link String} with the reporting
@@ -759,19 +777,21 @@
* @see android.content.pm.LauncherApps#getAppUsageLimit
*
* @param observerId A unique id associated with the group of apps to be monitored. There can
- * be multiple groups with common packages and different time limits.
+ * be multiple groups with common packages and different time limits.
* @param observedEntities The list of packages and token to observe for usage time. Cannot be
* null and must include at least one package or token.
* @param timeLimit The total time the set of apps can be in the foreground before the
- * callbackIntent is delivered. Must be at least one minute. Note: a limit of
- * 0 can be set to indicate that the user has already exhausted the limit for
- * a group, in which case, the given {@code callbackIntent} will be ignored.
- * @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
+ * {@code callbackIntent} is delivered. Must be at least one minute.
+ * @param timeRemaining The remaining time the set of apps can be in the foreground before the
+ * {@code callbackIntent} is delivered. Must be greater than
+ * {@code timeLimit}. Note: a limit of 0 can be set to indicate that the
+ * user has already exhausted the limit for a group, in which case,
+ * the given {@code callbackIntent} will be ignored.
* @param callbackIntent The PendingIntent that will be dispatched when the usage limit is
* exceeded by the group of apps. The delivered Intent will also contain
* the extras {@link #EXTRA_OBSERVER_ID}, {@link #EXTRA_TIME_LIMIT} and
* {@link #EXTRA_TIME_USED}. Cannot be {@code null} unless the observer is
- * being registered with a {@code timeLimit} of 0.
+ * being registered with a {@code timeRemaining} of 0.
* @throws SecurityException if the caller doesn't have both SUSPEND_APPS and OBSERVE_APP_USAGE
* permissions.
* @hide
@@ -781,10 +801,12 @@
android.Manifest.permission.SUSPEND_APPS,
android.Manifest.permission.OBSERVE_APP_USAGE})
public void registerAppUsageLimitObserver(int observerId, @NonNull String[] observedEntities,
- long timeLimit, @NonNull TimeUnit timeUnit, @Nullable PendingIntent callbackIntent) {
+ @NonNull Duration timeLimit, @NonNull Duration timeRemaining,
+ @Nullable PendingIntent callbackIntent) {
try {
mService.registerAppUsageLimitObserver(observerId, observedEntities,
- timeUnit.toMillis(timeLimit), callbackIntent, mContext.getOpPackageName());
+ timeLimit.toMillis(), timeRemaining.toMillis(), callbackIntent,
+ mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 536c2e1..23401432 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -678,6 +678,31 @@
public void onCameraUnavailable(@NonNull String cameraId) {
// default empty implementation
}
+
+ /**
+ * Notify registered clients about a change in the camera access priorities.
+ *
+ * <p>Notification that camera access priorities have changed and the camera may
+ * now be openable. An application that was previously denied camera access due to
+ * a higher-priority user already using the camera, or that was disconnected from an
+ * active camera session due to a higher-priority user trying to open the camera,
+ * should try to open the camera again if it still wants to use it. Note that
+ * multiple applications may receive this callback at the same time, and only one of
+ * them will succeed in opening the camera in practice, depending on exact access
+ * priority levels and timing. This method is useful in cases where multiple
+ * applications may be in the resumed state at the same time, and the user switches
+ * focus between them, or if the current camera-using application moves between
+ * full-screen and Picture-in-Picture (PiP) states. In such cases, the camera
+ * available/unavailable callbacks will not be invoked, but another application may
+ * now have higher priority for camera access than the current camera-using
+ * application.</p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ */
+ public void onCameraAccessPrioritiesChanged() {
+ // default empty implementation
+ }
}
/**
@@ -1098,6 +1123,22 @@
}
}
+ private void postSingleAccessPriorityChangeUpdate(final AvailabilityCallback callback,
+ final Executor executor) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(
+ new Runnable() {
+ @Override
+ public void run() {
+ callback.onCameraAccessPrioritiesChanged();
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor,
final String id, final int status) {
if (isAvailable(status)) {
@@ -1347,6 +1388,19 @@
}
}
+ @Override
+ public void onCameraAccessPrioritiesChanged() {
+ synchronized (mLock) {
+ final int callbackCount = mCallbackMap.size();
+ for (int i = 0; i < callbackCount; i++) {
+ Executor executor = mCallbackMap.valueAt(i);
+ final AvailabilityCallback callback = mCallbackMap.keyAt(i);
+
+ postSingleAccessPriorityChangeUpdate(callback, executor);
+ }
+ }
+ }
+
/**
* Try to connect to camera service after some delay if any client registered camera
* availability callback or torch status callback.
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index cb2517e..76e911d 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -40,6 +40,7 @@
void getPermissionUsages(boolean countSystem, long numMillis, in RemoteCallback callback);
void isApplicationQualifiedForRole(String roleName, String packageName,
in RemoteCallback callback);
+ void isRoleVisible(String roleName, in RemoteCallback callback);
void setRuntimePermissionGrantStateByDeviceAdmin(String callerPackageName, String packageName,
String permission, int grantState, in RemoteCallback callback);
}
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 9d58064..5695e42 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -476,6 +476,26 @@
}
/**
+ * Check whether a role should be visible to user.
+ *
+ * @param roleName name of the role to check for
+ * @param executor Executor on which to invoke the callback
+ * @param callback Callback to receive the result
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+ public void isRoleVisible(@NonNull String roleName,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+ checkStringNotEmpty(roleName);
+ checkNotNull(executor);
+ checkNotNull(callback);
+
+ mRemoteService.scheduleRequest(new PendingIsRoleVisibleRequest(mRemoteService, roleName,
+ executor, callback));
+ }
+
+ /**
* A connection to the remote service
*/
static final class RemoteService extends
@@ -1222,4 +1242,55 @@
}
}
}
+
+ /**
+ * Request for {@link #isRoleVisible}.
+ */
+ private static final class PendingIsRoleVisibleRequest extends
+ AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
+
+ private final @NonNull String mRoleName;
+ private final @NonNull Consumer<Boolean> mCallback;
+
+ private final @NonNull RemoteCallback mRemoteCallback;
+
+ private PendingIsRoleVisibleRequest(@NonNull RemoteService service,
+ @NonNull String roleName, @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Boolean> callback) {
+ super(service);
+
+ mRoleName = roleName;
+ mCallback = callback;
+
+ mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
+ long token = Binder.clearCallingIdentity();
+ try {
+ boolean visible;
+ if (result != null) {
+ visible = result.getBoolean(KEY_RESULT);
+ } else {
+ visible = false;
+ }
+ callback.accept(visible);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ finish();
+ }
+ }), null);
+ }
+
+ @Override
+ protected void onTimeout(RemoteService remoteService) {
+ mCallback.accept(false);
+ }
+
+ @Override
+ public void run() {
+ try {
+ getService().getServiceInterface().isRoleVisible(mRoleName, mRemoteCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error checking whether role should be visible", e);
+ }
+ }
+ }
}
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index ee03689..d375c10 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -179,12 +179,21 @@
* @param roleName name of the role to check for
* @param packageName package name of the application to check for
*
- * @return whether the application is qualified for the role.
+ * @return whether the application is qualified for the role
*/
public abstract boolean onIsApplicationQualifiedForRole(@NonNull String roleName,
@NonNull String packageName);
/**
+ * Check whether a role should be visible to user.
+ *
+ * @param roleName name of the role to check for
+ *
+ * @return whether the role should be visible to user
+ */
+ public abstract boolean onIsRoleVisible(@NonNull String roleName);
+
+ /**
* Set the runtime permission state from a device admin.
*
* @param callerPackageName The package name of the admin requesting the change
@@ -344,6 +353,18 @@
}
@Override
+ public void isRoleVisible(String roleName, RemoteCallback callback) {
+ checkStringNotEmpty(roleName);
+ checkNotNull(callback, "callback");
+
+ enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null);
+
+ mHandler.sendMessage(obtainMessage(
+ PermissionControllerService::isRoleVisible,
+ PermissionControllerService.this, roleName, callback));
+ }
+
+ @Override
public void setRuntimePermissionGrantStateByDeviceAdmin(String callerPackageName,
String packageName, String permission, int grantState,
RemoteCallback callback) {
@@ -445,6 +466,13 @@
callback.sendResult(result);
}
+ private void isRoleVisible(@NonNull String roleName, @NonNull RemoteCallback callback) {
+ boolean visible = onIsRoleVisible(roleName);
+ Bundle result = new Bundle();
+ result.putBoolean(PermissionControllerManager.KEY_RESULT, visible);
+ callback.sendResult(result);
+ }
+
private void setRuntimePermissionGrantStateByDeviceAdmin(@NonNull String callerPackageName,
@NonNull String packageName, @NonNull String permission,
@PermissionGrantState int grantState, @NonNull RemoteCallback callback) {
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 2cd3c48..5d4539c 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -64,12 +64,14 @@
public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager";
/**
- * Namespace for all Game Driver features.
+ * Namespace for all activity manager related features that are used at the native level.
+ * These features are applied at reboot.
*
* @hide
*/
@SystemApi
- public static final String NAMESPACE_GAME_DRIVER = "game_driver";
+ public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT =
+ "activity_manager_native_boot";
/**
* Namespace for autofill feature that provides suggestions across all apps when
@@ -92,6 +94,14 @@
public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
/**
+ * Namespace for all Game Driver features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_GAME_DRIVER = "game_driver";
+
+ /**
* Namespace for all input-related features that are used at the native level.
* These features are applied at reboot.
*
@@ -101,6 +111,14 @@
public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
/**
+ * Namespace for all media native related features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
+
+ /**
* Namespace for all netd related features.
*
* @hide
@@ -109,6 +127,15 @@
public static final String NAMESPACE_NETD_NATIVE = "netd_native";
/**
+ * Namespace for all runtime native boot related features. Boot in this case refers to the
+ * fact that the properties only take affect after rebooting the device.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
+
+ /**
* Namespace for System UI related features.
*
* @hide
@@ -174,40 +201,6 @@
}
/**
- * Namespace for all runtime native boot related features. Boot in this case refers to the
- * fact that the properties only take affect after rebooting the device.
- *
- * @hide
- */
- @SystemApi
- public interface RuntimeNativeBoot {
- String NAMESPACE = "runtime_native_boot";
- }
-
- /**
- * Namespace for all media native related features.
- *
- * @hide
- */
- @SystemApi
- public interface MediaNative {
- /** The flag namespace for media native features. */
- String NAMESPACE = "media_native";
- }
-
- /**
- * Namespace for all activity manager related features that are used at the native level.
- * These features are applied at reboot.
- *
- * @hide
- */
- @SystemApi
- public interface ActivityManagerNativeBoot {
- String NAMESPACE = "activity_manager_native_boot";
- String OFFLOAD_QUEUE_ENABLED = "offload_queue_enabled";
- }
-
- /**
* Namespace for attention-based features provided by on-device machine intelligence.
*
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 368fc2a..45219d6 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3321,6 +3321,17 @@
ColorDisplayManager.COLOR_MODE_AUTOMATIC);
/**
+ * The user selected peak refresh rate in frames per second.
+ *
+ * If this isn't set, the system falls back to a device specific default.
+ * @hide
+ */
+ public static final String PEAK_REFRESH_RATE = "peak_refresh_rate";
+
+ private static final Validator PEAK_REFRESH_RATE_VALIDATOR =
+ new SettingsValidators.InclusiveFloatRangeValidator(24f, Float.MAX_VALUE);
+
+ /**
* The amount of time in milliseconds before the device goes to sleep or begins
* to dream after a period of inactivity. This value is also known as the
* user activity timeout period since the screen isn't necessarily turned off
diff --git a/core/java/android/service/carrier/ApnService.java b/core/java/android/service/carrier/ApnService.java
index d53eb37..57e4b1b 100644
--- a/core/java/android/service/carrier/ApnService.java
+++ b/core/java/android/service/carrier/ApnService.java
@@ -16,6 +16,8 @@
package android.service.carrier;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.WorkerThread;
import android.app.Service;
@@ -60,7 +62,8 @@
};
@Override
- public IBinder onBind(Intent intent) {
+ @NonNull
+ public IBinder onBind(@Nullable Intent intent) {
return mBinder;
}
@@ -73,5 +76,6 @@
* subId.
*/
@WorkerThread
+ @NonNull
public abstract List<ContentValues> onRestoreApns(int subId);
}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index f3bc2c7..5064d23 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2287,4 +2287,7 @@
// OPEN: Accessibility detail settings (android.settings.ACCESSIBILITY_DETAILS_SETTINGS intent)
ACCESSIBILITY_DETAILS_SETTINGS = 1682;
+
+ // Open: Settings will show the conditional when Grayscale mode is on
+ SETTINGS_CONDITION_GRAYSCALE_MODE = 1683;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e87295a..ae3e714 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4445,15 +4445,23 @@
@hide -->
<permission android:name="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY"
android:protectionLevel="signature|preinstalled" />
+
<!-- @SystemApi Allows wallpaper to be rendered in ambient mode.
@hide -->
<permission android:name="android.permission.AMBIENT_WALLPAPER"
android:protectionLevel="signature|preinstalled" />
+
<!-- @SystemApi Allows sensor privacy to be modified.
@hide -->
<permission android:name="android.permission.MANAGE_SENSOR_PRIVACY"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows an activity to replace the app name and icon displayed in share targets
+ in the sharesheet for the Q-release and later.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON"
+ android:protectionLevel="signature|privileged" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 47e5462..2f2fdca 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3945,4 +3945,7 @@
and a second time clipped to the fill level to indicate charge -->
<bool name="config_batterymeterDualTone">false</bool>
+ <!-- The default peak refresh rate for a given device. Change this value if you want to allow
+ for higher refresh rates to be automatically used out of the box -->
+ <integer name="config_defaultPeakRefreshRate">60</integer>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 426d813..d5444c0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3688,4 +3688,6 @@
<java-symbol type="string" name="mime_type_spreadsheet_ext" />
<java-symbol type="string" name="mime_type_presentation" />
<java-symbol type="string" name="mime_type_presentation_ext" />
+
+ <java-symbol type="integer" name="config_defaultPeakRefreshRate" />
</resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 69632c1..ebc6be7 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -92,7 +92,8 @@
Settings.System.VOLUME_SYSTEM, // deprecated since API 2?
Settings.System.VOLUME_VOICE, // deprecated since API 2?
Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
- Settings.System.WINDOW_ORIENTATION_LISTENER_LOG // used for debugging only
+ Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
+ Settings.System.PEAK_REFRESH_RATE // depends on hardware capabilities
);
private static final Set<String> BACKUP_BLACKLISTED_GLOBAL_SETTINGS =
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index 2110a8f..3e53a38 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -33,6 +33,7 @@
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE" />
<permission name="android.permission.MASTER_CLEAR"/>
+ <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<permission name="android.permission.MOVE_PACKAGE"/>
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 117d05e..d74f27c 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -1052,6 +1052,8 @@
}
void AssetManager2::InvalidateCaches(uint32_t diff) {
+ cached_bag_resid_stacks_.clear();
+
if (diff == 0xffffffffu) {
// Everything must go.
cached_bags_.clear();
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index ed74333..17e2509 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -113,10 +113,6 @@
* Bit mask for mFieldsMask indicating the presence of mBearingAccuracy.
*/
private static final int HAS_BEARING_ACCURACY_MASK = 128;
- /**
- * Bit mask for mFieldsMask indicating the presence of mElapsedRealtimeUncertaintyNanos.
- */
- private static final int HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK = 256;
// Cached data to make bearing/distance computations more efficient for the case
// where distanceTo and bearingTo are called in sequence. Assume this typically happens
@@ -134,9 +130,6 @@
private long mTime = 0;
@UnsupportedAppUsage
private long mElapsedRealtimeNanos = 0;
- // Estimate of the relative precision of the alignment of this SystemClock
- // timestamp, with the reported measurements in nanoseconds (68% confidence).
- private long mElapsedRealtimeUncertaintyNanos = 0;
private double mLatitude = 0.0;
private double mLongitude = 0.0;
private double mAltitude = 0.0f;
@@ -178,7 +171,6 @@
mProvider = l.mProvider;
mTime = l.mTime;
mElapsedRealtimeNanos = l.mElapsedRealtimeNanos;
- mElapsedRealtimeUncertaintyNanos = l.mElapsedRealtimeUncertaintyNanos;
mFieldsMask = l.mFieldsMask;
mLatitude = l.mLatitude;
mLongitude = l.mLongitude;
@@ -199,7 +191,6 @@
mProvider = null;
mTime = 0;
mElapsedRealtimeNanos = 0;
- mElapsedRealtimeUncertaintyNanos = 0;
mFieldsMask = 0;
mLatitude = 0;
mLongitude = 0;
@@ -595,37 +586,6 @@
}
/**
- * Get estimate of the relative precision of the alignment of the
- * ElapsedRealtimeNanos timestamp, with the reported measurements in
- * nanoseconds (68% confidence).
- *
- * @return uncertainty of elapsed real-time of fix, in nanoseconds.
- */
- public long getElapsedRealtimeUncertaintyNanos() {
- return mElapsedRealtimeUncertaintyNanos;
- }
-
- /**
- * Set estimate of the relative precision of the alignment of the
- * ElapsedRealtimeNanos timestamp, with the reported measurements in
- * nanoseconds (68% confidence).
- *
- * @param time uncertainty of the elapsed real-time of fix, in nanoseconds.
- */
- public void setElapsedRealtimeUncertaintyNanos(long time) {
- mElapsedRealtimeUncertaintyNanos = time;
- mFieldsMask |= HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK;
- }
-
- /**
- * True if this location has a elapsed realtime accuracy.
- */
- public boolean hasElapsedRealtimeUncertaintyNanos() {
- return (mFieldsMask & HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK) != 0;
- }
-
-
- /**
* Get the latitude, in degrees.
*
* <p>All locations generated by the {@link LocationManager}
@@ -1102,10 +1062,6 @@
s.append(" et=");
TimeUtils.formatDuration(mElapsedRealtimeNanos / 1000000L, s);
}
- if (hasElapsedRealtimeUncertaintyNanos()) {
- s.append(" etAcc=");
- TimeUtils.formatDuration(mElapsedRealtimeUncertaintyNanos / 1000000L, s);
- }
if (hasAltitude()) s.append(" alt=").append(mAltitude);
if (hasSpeed()) s.append(" vel=").append(mSpeed);
if (hasBearing()) s.append(" bear=").append(mBearing);
@@ -1136,7 +1092,6 @@
Location l = new Location(provider);
l.mTime = in.readLong();
l.mElapsedRealtimeNanos = in.readLong();
- l.mElapsedRealtimeUncertaintyNanos = in.readLong();
l.mFieldsMask = in.readByte();
l.mLatitude = in.readDouble();
l.mLongitude = in.readDouble();
@@ -1167,7 +1122,6 @@
parcel.writeString(mProvider);
parcel.writeLong(mTime);
parcel.writeLong(mElapsedRealtimeNanos);
- parcel.writeLong(mElapsedRealtimeUncertaintyNanos);
parcel.writeByte(mFieldsMask);
parcel.writeDouble(mLatitude);
parcel.writeDouble(mLongitude);
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 15f9b47..669baea 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4068,6 +4068,10 @@
/**
* Indicate Hearing Aid connection state change and eventually suppress
* the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
+ * This operation is asynchronous but its execution will still be sequentially scheduled
+ * relative to calls to {@link #setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ * * BluetoothDevice, int, int, boolean, int)} and
+ * and {@link #handleBluetoothA2dpDeviceConfigChange(BluetoothDevice)}.
* @param device Bluetooth device connected/disconnected
* @param state new connection state (BluetoothProfile.STATE_xxx)
* @param musicDevice Default get system volume for the connecting device.
@@ -4075,27 +4079,27 @@
* {@link android.bluetooth.BluetoothProfile.HEARING_AID})
* @param suppressNoisyIntent if true the
* {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
- * @return a delay in ms that the caller should wait before broadcasting
- * BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED intent.
* {@hide}
*/
- public int setBluetoothHearingAidDeviceConnectionState(
+ public void setBluetoothHearingAidDeviceConnectionState(
BluetoothDevice device, int state, boolean suppressNoisyIntent,
int musicDevice) {
final IAudioService service = getService();
- int delay = 0;
try {
- delay = service.setBluetoothHearingAidDeviceConnectionState(device,
+ service.setBluetoothHearingAidDeviceConnectionState(device,
state, suppressNoisyIntent, musicDevice);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- return delay;
}
/**
* Indicate A2DP source or sink connection state change and eventually suppress
* the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
+ * This operation is asynchronous but its execution will still be sequentially scheduled
+ * relative to calls to {@link #setBluetoothHearingAidDeviceConnectionState(BluetoothDevice,
+ * int, boolean, int)} and
+ * {@link #handleBluetoothA2dpDeviceConfigChange(BluetoothDevice)}.
* @param device Bluetooth device connected/disconnected
* @param state new connection state, {@link BluetoothProfile#STATE_CONNECTED}
* or {@link BluetoothProfile#STATE_DISCONNECTED}
@@ -4105,26 +4109,27 @@
* {@link android.bluetooth.BluetoothProfile.A2DP_SINK})
* @param suppressNoisyIntent if true the
* {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
- * @return a delay in ms that the caller should wait before broadcasting
- * BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED intent.
* {@hide}
*/
- public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
BluetoothDevice device, int state,
int profile, boolean suppressNoisyIntent, int a2dpVolume) {
final IAudioService service = getService();
- int delay = 0;
try {
- delay = service.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device,
+ service.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device,
state, profile, suppressNoisyIntent, a2dpVolume);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- return delay;
}
/**
* Indicate A2DP device configuration has changed.
+ * This operation is asynchronous but its execution will still be sequentially scheduled
+ * relative to calls to
+ * {@link #setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice, int, int,
+ * boolean, int)} and
+ * {@link #setBluetoothHearingAidDeviceConnectionState(BluetoothDevice, int, boolean, int)}
* @param device Bluetooth device whose configuration has changed.
* {@hide}
*/
diff --git a/media/java/android/media/HwAudioSource.java b/media/java/android/media/HwAudioSource.java
index 8bdb8a6..d9be0a5 100644
--- a/media/java/android/media/HwAudioSource.java
+++ b/media/java/android/media/HwAudioSource.java
@@ -165,7 +165,7 @@
* If the audio attributes are not set with {@link #setAudioAttributes(AudioAttributes)},
* attributes comprising {@link AudioAttributes#USAGE_MEDIA} will be used.
*/
- public static class Builder {
+ public static final class Builder {
private AudioAttributes mAudioAttributes;
private AudioDeviceInfo mAudioDeviceInfo;
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 1b82fcc..0285667 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -228,10 +228,10 @@
oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio);
- int setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device,
+ void setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device,
int state, boolean suppressNoisyIntent, int musicDevice);
- int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device,
+ void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device,
int state, int profile, boolean suppressNoisyIntent, int a2dpVolume);
oneway void setFocusRequestResultFromExtPolicy(in AudioFocusInfo afi, int requestResult,
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index fbe5152..b302b06 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -571,13 +571,12 @@
// Utilities
/**
+ * @hide
* Use to generate warning or exception in legacy code paths that allowed passing stream types
* to qualify audio playback.
* @param streamType the stream type to check
* @throws IllegalArgumentException
- * @deprecated This method is not intended to be used by applications.
*/
- @java.lang.Deprecated
public static void deprecateStreamTypeForPlayback(int streamType, @NonNull String className,
@NonNull String opName) throws IllegalArgumentException {
// STREAM_ACCESSIBILITY was introduced at the same time the use of stream types
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategies.java b/media/java/android/media/audiopolicy/AudioProductStrategies.java
index 98b6d97..c305b68 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategies.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategies.java
@@ -85,7 +85,7 @@
* Returns an {@link Iterator}
*/
@Override
- public Iterator<AudioProductStrategy> iterator() {
+ public @NonNull Iterator<AudioProductStrategy> iterator() {
return mAudioProductStrategyList.iterator();
}
diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroup.java b/media/java/android/media/audiopolicy/AudioVolumeGroup.java
index 0b4ba93..b60947f 100644
--- a/media/java/android/media/audiopolicy/AudioVolumeGroup.java
+++ b/media/java/android/media/audiopolicy/AudioVolumeGroup.java
@@ -77,7 +77,7 @@
/**
* @return List of {@link AudioAttributes} involved in this {@link AudioVolumeGroup}.
*/
- public List<AudioAttributes> getAudioAttributes() {
+ public @NonNull List<AudioAttributes> getAudioAttributes() {
return Arrays.asList(mAudioAttributes);
}
diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroups.java b/media/java/android/media/audiopolicy/AudioVolumeGroups.java
index 301bec7..2e56f84 100644
--- a/media/java/android/media/audiopolicy/AudioVolumeGroups.java
+++ b/media/java/android/media/audiopolicy/AudioVolumeGroups.java
@@ -70,7 +70,7 @@
* Returns an {@link Iterator}
*/
@Override
- public Iterator<AudioVolumeGroup> iterator() {
+ public @NonNull Iterator<AudioVolumeGroup> iterator() {
return mAudioVolumeGroupList.iterator();
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index d8c975f..87a59df 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -309,6 +309,10 @@
Log.v(TAG, String.format("Camera %s has torch status changed to 0x%x",
cameraId, status));
}
+ @Override
+ public void onCameraAccessPrioritiesChanged() {
+ Log.v(TAG, "Camera access permission change");
+ }
}
/**
diff --git a/packages/SystemUI/res/drawable/ic_volume_remote.xml b/packages/SettingsLib/res/drawable/ic_volume_remote.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_volume_remote.xml
rename to packages/SettingsLib/res/drawable/ic_volume_remote.xml
diff --git a/packages/SystemUI/res/drawable/ic_volume_remote_mute.xml b/packages/SettingsLib/res/drawable/ic_volume_remote_mute.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_volume_remote_mute.xml
rename to packages/SettingsLib/res/drawable/ic_volume_remote_mute.xml
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/D.java b/packages/SettingsLib/src/com/android/settingslib/volume/D.java
new file mode 100644
index 0000000..7e0654d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/D.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package com.android.settingslib.volume;
+
+import android.util.Log;
+
+class D {
+ public static boolean BUG = Log.isLoggable("volume", Log.DEBUG);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java b/packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.java
similarity index 84%
rename from packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java
rename to packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.java
index 8b00eee..4ed1154 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.volume;
+package com.android.settingslib.volume;
import android.app.PendingIntent;
import android.content.Context;
@@ -27,7 +27,6 @@
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaController.PlaybackInfo;
-import android.media.session.MediaSession;
import android.media.session.MediaSession.QueueItem;
import android.media.session.MediaSession.Token;
import android.media.session.MediaSessionManager;
@@ -41,7 +40,6 @@
import android.util.Log;
import java.io.PrintWriter;
-import java.io.StringWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -73,16 +71,24 @@
mCallbacks = callbacks;
}
+ /**
+ * Dump to {@code writer}
+ */
public void dump(PrintWriter writer) {
writer.println(getClass().getSimpleName() + " state:");
- writer.print(" mInit: "); writer.println(mInit);
- writer.print(" mRecords.size: "); writer.println(mRecords.size());
+ writer.print(" mInit: ");
+ writer.println(mInit);
+ writer.print(" mRecords.size: ");
+ writer.println(mRecords.size());
int i = 0;
for (MediaControllerRecord r : mRecords.values()) {
dump(++i, writer, r.controller);
}
}
+ /**
+ * init MediaSessions
+ */
public void init() {
if (D.BUG) Log.d(TAG, "init");
// will throw if no permission
@@ -97,12 +103,18 @@
mHandler.sendEmptyMessage(H.UPDATE_SESSIONS);
}
+ /**
+ * Destroy MediaSessions
+ */
public void destroy() {
if (D.BUG) Log.d(TAG, "destroy");
mInit = false;
mMgr.removeOnActiveSessionsChangedListener(mSessionsListener);
}
+ /**
+ * Set volume {@code level} to remote media {@code token}
+ */
public void setVolume(Token token, int level) {
final MediaControllerRecord r = mRecords.get(token);
if (r == null) {
@@ -113,15 +125,17 @@
r.controller.setVolumeTo(level, 0);
}
- private void onRemoteVolumeChangedH(MediaSession.Token sessionToken, int flags) {
+ private void onRemoteVolumeChangedH(Token sessionToken, int flags) {
final MediaController controller = new MediaController(mContext, sessionToken);
- if (D.BUG) Log.d(TAG, "remoteVolumeChangedH " + controller.getPackageName() + " "
- + Util.audioManagerFlagsToString(flags));
+ if (D.BUG) {
+ Log.d(TAG, "remoteVolumeChangedH " + controller.getPackageName() + " "
+ + Util.audioManagerFlagsToString(flags));
+ }
final Token token = controller.getSessionToken();
mCallbacks.onRemoteVolumeChanged(token, flags);
}
- private void onUpdateRemoteControllerH(MediaSession.Token sessionToken) {
+ private void onUpdateRemoteControllerH(Token sessionToken) {
final MediaController controller =
sessionToken != null ? new MediaController(mContext, sessionToken) : null;
final String pkg = controller != null ? controller.getPackageName() : null;
@@ -191,7 +205,8 @@
if (appLabel.length() > 0) {
return appLabel;
}
- } catch (NameNotFoundException e) { }
+ } catch (NameNotFoundException e) {
+ }
return pkg;
}
@@ -240,29 +255,11 @@
}
}
- public static void dumpMediaSessions(Context context) {
- final MediaSessionManager mgr = (MediaSessionManager) context
- .getSystemService(Context.MEDIA_SESSION_SERVICE);
- try {
- final List<MediaController> controllers = mgr.getActiveSessions(null);
- final int N = controllers.size();
- if (D.BUG) Log.d(TAG, N + " controllers");
- for (int i = 0; i < N; i++) {
- final StringWriter sw = new StringWriter();
- final PrintWriter pw = new PrintWriter(sw, true);
- dump(i + 1, pw, controllers.get(i));
- if (D.BUG) Log.d(TAG, sw.toString());
- }
- } catch (SecurityException e) {
- Log.w(TAG, "Not allowed to get sessions", e);
- }
- }
-
private final class MediaControllerRecord extends MediaController.Callback {
- private final MediaController controller;
+ public final MediaController controller;
- private boolean sentRemote;
- private String name;
+ public boolean sentRemote;
+ public String name;
private MediaControllerRecord(MediaController controller) {
this.controller = controller;
@@ -274,8 +271,10 @@
@Override
public void onAudioInfoChanged(PlaybackInfo info) {
- if (D.BUG) Log.d(TAG, cb("onAudioInfoChanged") + Util.playbackInfoToString(info)
- + " sentRemote=" + sentRemote);
+ if (D.BUG) {
+ Log.d(TAG, cb("onAudioInfoChanged") + Util.playbackInfoToString(info)
+ + " sentRemote=" + sentRemote);
+ }
final boolean remote = isRemote(info);
if (!remote && sentRemote) {
mCallbacks.onRemoteRemoved(controller.getSessionToken());
@@ -324,22 +323,22 @@
private final OnActiveSessionsChangedListener mSessionsListener =
new OnActiveSessionsChangedListener() {
- @Override
- public void onActiveSessionsChanged(List<MediaController> controllers) {
- onActiveSessionsUpdatedH(controllers);
- }
- };
+ @Override
+ public void onActiveSessionsChanged(List<MediaController> controllers) {
+ onActiveSessionsUpdatedH(controllers);
+ }
+ };
private final IRemoteVolumeController mRvc = new IRemoteVolumeController.Stub() {
@Override
- public void remoteVolumeChanged(MediaSession.Token sessionToken, int flags)
+ public void remoteVolumeChanged(Token sessionToken, int flags)
throws RemoteException {
mHandler.obtainMessage(H.REMOTE_VOLUME_CHANGED, flags, 0,
sessionToken).sendToTarget();
}
@Override
- public void updateRemoteController(final MediaSession.Token sessionToken)
+ public void updateRemoteController(final Token sessionToken)
throws RemoteException {
mHandler.obtainMessage(H.UPDATE_REMOTE_CONTROLLER, sessionToken).sendToTarget();
}
@@ -361,18 +360,32 @@
onActiveSessionsUpdatedH(mMgr.getActiveSessions(null));
break;
case REMOTE_VOLUME_CHANGED:
- onRemoteVolumeChangedH((MediaSession.Token) msg.obj, msg.arg1);
+ onRemoteVolumeChangedH((Token) msg.obj, msg.arg1);
break;
case UPDATE_REMOTE_CONTROLLER:
- onUpdateRemoteControllerH((MediaSession.Token) msg.obj);
+ onUpdateRemoteControllerH((Token) msg.obj);
break;
}
}
}
+ /**
+ * Callback for remote media sessions
+ */
public interface Callbacks {
+ /**
+ * Invoked when remote media session is updated
+ */
void onRemoteUpdate(Token token, String name, PlaybackInfo pi);
+
+ /**
+ * Invoked when remote media session is removed
+ */
void onRemoteRemoved(Token t);
+
+ /**
+ * Invoked when remote volume is changed
+ */
void onRemoteVolumeChanged(Token token, int flags);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/Util.java b/packages/SettingsLib/src/com/android/settingslib/volume/Util.java
new file mode 100644
index 0000000..4e770ae
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/Util.java
@@ -0,0 +1,187 @@
+/*
+ * 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.
+ */
+
+package com.android.settingslib.volume;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.MediaMetadata;
+import android.media.VolumeProvider;
+import android.media.session.MediaController.PlaybackInfo;
+import android.media.session.PlaybackState;
+import android.telephony.TelephonyManager;
+import android.widget.TextView;
+
+import java.util.Objects;
+
+/**
+ * Static helpers for the volume dialog.
+ */
+public class Util {
+
+ private static final int[] AUDIO_MANAGER_FLAGS = new int[]{
+ AudioManager.FLAG_SHOW_UI,
+ AudioManager.FLAG_VIBRATE,
+ AudioManager.FLAG_PLAY_SOUND,
+ AudioManager.FLAG_ALLOW_RINGER_MODES,
+ AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE,
+ AudioManager.FLAG_SHOW_VIBRATE_HINT,
+ AudioManager.FLAG_SHOW_SILENT_HINT,
+ AudioManager.FLAG_FROM_KEY,
+ AudioManager.FLAG_SHOW_UI_WARNINGS,
+ };
+
+ private static final String[] AUDIO_MANAGER_FLAG_NAMES = new String[]{
+ "SHOW_UI",
+ "VIBRATE",
+ "PLAY_SOUND",
+ "ALLOW_RINGER_MODES",
+ "REMOVE_SOUND_AND_VIBRATE",
+ "SHOW_VIBRATE_HINT",
+ "SHOW_SILENT_HINT",
+ "FROM_KEY",
+ "SHOW_UI_WARNINGS",
+ };
+
+ /**
+ * Extract log tag from {@code c}
+ */
+ public static String logTag(Class<?> c) {
+ final String tag = "vol." + c.getSimpleName();
+ return tag.length() < 23 ? tag : tag.substring(0, 23);
+ }
+
+ /**
+ * Convert media metadata to string
+ */
+ public static String mediaMetadataToString(MediaMetadata metadata) {
+ if (metadata == null) return null;
+ return metadata.getDescription().toString();
+ }
+
+ /**
+ * Convert playback info to string
+ */
+ public static String playbackInfoToString(PlaybackInfo info) {
+ if (info == null) return null;
+ final String type = playbackInfoTypeToString(info.getPlaybackType());
+ final String vc = volumeProviderControlToString(info.getVolumeControl());
+ return String.format("PlaybackInfo[vol=%s,max=%s,type=%s,vc=%s],atts=%s",
+ info.getCurrentVolume(), info.getMaxVolume(), type, vc, info.getAudioAttributes());
+ }
+
+ /**
+ * Convert type of playback info to string
+ */
+ public static String playbackInfoTypeToString(int type) {
+ switch (type) {
+ case PlaybackInfo.PLAYBACK_TYPE_LOCAL:
+ return "LOCAL";
+ case PlaybackInfo.PLAYBACK_TYPE_REMOTE:
+ return "REMOTE";
+ default:
+ return "UNKNOWN_" + type;
+ }
+ }
+
+ /**
+ * Convert state of playback info to string
+ */
+ public static String playbackStateStateToString(int state) {
+ switch (state) {
+ case PlaybackState.STATE_NONE:
+ return "STATE_NONE";
+ case PlaybackState.STATE_STOPPED:
+ return "STATE_STOPPED";
+ case PlaybackState.STATE_PAUSED:
+ return "STATE_PAUSED";
+ case PlaybackState.STATE_PLAYING:
+ return "STATE_PLAYING";
+ default:
+ return "UNKNOWN_" + state;
+ }
+ }
+
+ /**
+ * Convert volume provider control to string
+ */
+ public static String volumeProviderControlToString(int control) {
+ switch (control) {
+ case VolumeProvider.VOLUME_CONTROL_ABSOLUTE:
+ return "VOLUME_CONTROL_ABSOLUTE";
+ case VolumeProvider.VOLUME_CONTROL_FIXED:
+ return "VOLUME_CONTROL_FIXED";
+ case VolumeProvider.VOLUME_CONTROL_RELATIVE:
+ return "VOLUME_CONTROL_RELATIVE";
+ default:
+ return "VOLUME_CONTROL_UNKNOWN_" + control;
+ }
+ }
+
+ /**
+ * Convert {@link PlaybackState} to string
+ */
+ public static String playbackStateToString(PlaybackState playbackState) {
+ if (playbackState == null) return null;
+ return playbackStateStateToString(playbackState.getState()) + " " + playbackState;
+ }
+
+ /**
+ * Convert audio manager flags to string
+ */
+ public static String audioManagerFlagsToString(int value) {
+ return bitFieldToString(value, AUDIO_MANAGER_FLAGS, AUDIO_MANAGER_FLAG_NAMES);
+ }
+
+ protected static String bitFieldToString(int value, int[] values, String[] names) {
+ if (value == 0) return "";
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < values.length; i++) {
+ if ((value & values[i]) != 0) {
+ if (sb.length() > 0) sb.append(',');
+ sb.append(names[i]);
+ }
+ value &= ~values[i];
+ }
+ if (value != 0) {
+ if (sb.length() > 0) sb.append(',');
+ sb.append("UNKNOWN_").append(value);
+ }
+ return sb.toString();
+ }
+
+ private static CharSequence emptyToNull(CharSequence str) {
+ return str == null || str.length() == 0 ? null : str;
+ }
+
+ /**
+ * Set text for specific {@link TextView}
+ */
+ public static boolean setText(TextView tv, CharSequence text) {
+ if (Objects.equals(emptyToNull(tv.getText()), emptyToNull(text))) return false;
+ tv.setText(text);
+ return true;
+ }
+
+ /**
+ * Return {@code true} if it is voice capable
+ */
+ public static boolean isVoiceCapable(Context context) {
+ final TelephonyManager telephony =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ return telephony != null && telephony.isVoiceCapable();
+ }
+}
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png
deleted file mode 100644
index 035a4df..0000000
--- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 563b007..507c822 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -148,6 +148,7 @@
Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
Dependency.get(SysuiColorExtractor.class)
.removeOnColorsChangedListener(mColorsListener);
+ setClockPlugin(null);
}
private void setClockPlugin(ClockPlugin plugin) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Util.java b/packages/SystemUI/src/com/android/systemui/volume/Util.java
index c6d6218..7edb5a5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Util.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Util.java
@@ -16,52 +16,13 @@
package com.android.systemui.volume;
-import android.content.Context;
import android.media.AudioManager;
-import android.media.MediaMetadata;
-import android.media.VolumeProvider;
-import android.media.session.MediaController.PlaybackInfo;
-import android.media.session.PlaybackState;
-import android.telephony.TelephonyManager;
import android.view.View;
-import android.widget.TextView;
-
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.Objects;
/**
* Static helpers for the volume dialog.
*/
-class Util {
-
- // Note: currently not shown (only used in the text footer)
- private static final SimpleDateFormat HMMAA = new SimpleDateFormat("h:mm aa", Locale.US);
-
- private static int[] AUDIO_MANAGER_FLAGS = new int[] {
- AudioManager.FLAG_SHOW_UI,
- AudioManager.FLAG_VIBRATE,
- AudioManager.FLAG_PLAY_SOUND,
- AudioManager.FLAG_ALLOW_RINGER_MODES,
- AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE,
- AudioManager.FLAG_SHOW_VIBRATE_HINT,
- AudioManager.FLAG_SHOW_SILENT_HINT,
- AudioManager.FLAG_FROM_KEY,
- AudioManager.FLAG_SHOW_UI_WARNINGS,
- };
-
- private static String[] AUDIO_MANAGER_FLAG_NAMES = new String[] {
- "SHOW_UI",
- "VIBRATE",
- "PLAY_SOUND",
- "ALLOW_RINGER_MODES",
- "REMOVE_SOUND_AND_VIBRATE",
- "SHOW_VIBRATE_HINT",
- "SHOW_SILENT_HINT",
- "FROM_KEY",
- "SHOW_UI_WARNINGS",
- };
+class Util extends com.android.settingslib.volume.Util {
public static String logTag(Class<?> c) {
final String tag = "vol." + c.getSimpleName();
@@ -70,106 +31,19 @@
public static String ringerModeToString(int ringerMode) {
switch (ringerMode) {
- case AudioManager.RINGER_MODE_SILENT: return "RINGER_MODE_SILENT";
- case AudioManager.RINGER_MODE_VIBRATE: return "RINGER_MODE_VIBRATE";
- case AudioManager.RINGER_MODE_NORMAL: return "RINGER_MODE_NORMAL";
- default: return "RINGER_MODE_UNKNOWN_" + ringerMode;
+ case AudioManager.RINGER_MODE_SILENT:
+ return "RINGER_MODE_SILENT";
+ case AudioManager.RINGER_MODE_VIBRATE:
+ return "RINGER_MODE_VIBRATE";
+ case AudioManager.RINGER_MODE_NORMAL:
+ return "RINGER_MODE_NORMAL";
+ default:
+ return "RINGER_MODE_UNKNOWN_" + ringerMode;
}
}
- public static String mediaMetadataToString(MediaMetadata metadata) {
- if (metadata == null) return null;
- return metadata.getDescription().toString();
- }
-
- public static String playbackInfoToString(PlaybackInfo info) {
- if (info == null) return null;
- final String type = playbackInfoTypeToString(info.getPlaybackType());
- final String vc = volumeProviderControlToString(info.getVolumeControl());
- return String.format("PlaybackInfo[vol=%s,max=%s,type=%s,vc=%s],atts=%s",
- info.getCurrentVolume(), info.getMaxVolume(), type, vc, info.getAudioAttributes());
- }
-
- public static String playbackInfoTypeToString(int type) {
- switch (type) {
- case PlaybackInfo.PLAYBACK_TYPE_LOCAL: return "LOCAL";
- case PlaybackInfo.PLAYBACK_TYPE_REMOTE: return "REMOTE";
- default: return "UNKNOWN_" + type;
- }
- }
-
- public static String playbackStateStateToString(int state) {
- switch (state) {
- case PlaybackState.STATE_NONE: return "STATE_NONE";
- case PlaybackState.STATE_STOPPED: return "STATE_STOPPED";
- case PlaybackState.STATE_PAUSED: return "STATE_PAUSED";
- case PlaybackState.STATE_PLAYING: return "STATE_PLAYING";
- default: return "UNKNOWN_" + state;
- }
- }
-
- public static String volumeProviderControlToString(int control) {
- switch (control) {
- case VolumeProvider.VOLUME_CONTROL_ABSOLUTE: return "VOLUME_CONTROL_ABSOLUTE";
- case VolumeProvider.VOLUME_CONTROL_FIXED: return "VOLUME_CONTROL_FIXED";
- case VolumeProvider.VOLUME_CONTROL_RELATIVE: return "VOLUME_CONTROL_RELATIVE";
- default: return "VOLUME_CONTROL_UNKNOWN_" + control;
- }
- }
-
- public static String playbackStateToString(PlaybackState playbackState) {
- if (playbackState == null) return null;
- return playbackStateStateToString(playbackState.getState()) + " " + playbackState;
- }
-
- public static String audioManagerFlagsToString(int value) {
- return bitFieldToString(value, AUDIO_MANAGER_FLAGS, AUDIO_MANAGER_FLAG_NAMES);
- }
-
- private static String bitFieldToString(int value, int[] values, String[] names) {
- if (value == 0) return "";
- final StringBuilder sb = new StringBuilder();
- for (int i = 0; i < values.length; i++) {
- if ((value & values[i]) != 0) {
- if (sb.length() > 0) sb.append(',');
- sb.append(names[i]);
- }
- value &= ~values[i];
- }
- if (value != 0) {
- if (sb.length() > 0) sb.append(',');
- sb.append("UNKNOWN_").append(value);
- }
- return sb.toString();
- }
-
- public static String getShortTime(long millis) {
- return HMMAA.format(new Date(millis));
- }
-
- private static CharSequence emptyToNull(CharSequence str) {
- return str == null || str.length() == 0 ? null : str;
- }
-
- public static boolean setText(TextView tv, CharSequence text) {
- if (Objects.equals(emptyToNull(tv.getText()), emptyToNull(text))) return false;
- tv.setText(text);
- return true;
- }
-
public static final void setVisOrGone(View v, boolean vis) {
if (v == null || (v.getVisibility() == View.VISIBLE) == vis) return;
v.setVisibility(vis ? View.VISIBLE : View.GONE);
}
-
- public static final void setVisOrInvis(View v, boolean vis) {
- if (v == null || (v.getVisibility() == View.VISIBLE) == vis) return;
- v.setVisibility(vis ? View.VISIBLE : View.INVISIBLE);
- }
-
- public static boolean isVoiceCapable(Context context) {
- final TelephonyManager telephony =
- (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- return telephony != null && telephony.isVoiceCapable();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 32bc01c..4c16297 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -53,6 +53,7 @@
import android.view.accessibility.AccessibilityManager;
import com.android.internal.annotations.GuardedBy;
+import com.android.settingslib.volume.MediaSessions;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index ab8ad0f..0a6e923 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -7075,6 +7075,9 @@
// OPEN: Accessibility detail settings (android.settings.ACCESSIBILITY_DETAILS_SETTINGS intent)
ACCESSIBILITY_DETAILS_SETTINGS = 1682;
+ // Open: Settings will show the conditional when Grayscale mode is on
+ SETTINGS_CONDITION_GRAYSCALE_MODE = 1683;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 894a704..5da1ce6 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -78,11 +78,11 @@
// permission in the corresponding .te file your feature belongs to.
@VisibleForTesting
static final String[] sDeviceConfigScopes = new String[] {
- DeviceConfig.ActivityManagerNativeBoot.NAMESPACE,
- DeviceConfig.MediaNative.NAMESPACE,
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
+ DeviceConfig.NAMESPACE_MEDIA_NATIVE,
DeviceConfig.NAMESPACE_NETD_NATIVE,
- DeviceConfig.RuntimeNativeBoot.NAMESPACE,
+ DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
DeviceConfig.RuntimeNative.NAMESPACE,
};
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 9af57da..451fd66 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -201,51 +201,72 @@
}
}
- /*package*/ int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ private static final class BtDeviceConnectionInfo {
+ final @NonNull BluetoothDevice mDevice;
+ final @AudioService.BtProfileConnectionState int mState;
+ final int mProfile;
+ final boolean mSupprNoisy;
+ final int mVolume;
+
+ BtDeviceConnectionInfo(@NonNull BluetoothDevice device,
+ @AudioService.BtProfileConnectionState int state,
+ int profile, boolean suppressNoisyIntent, int vol) {
+ mDevice = device;
+ mState = state;
+ mProfile = profile;
+ mSupprNoisy = suppressNoisyIntent;
+ mVolume = vol;
+ }
+ }
+
+ /*package*/ void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
@NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
int profile, boolean suppressNoisyIntent, int a2dpVolume) {
- AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
- "setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent state=" + state
- // only querying address as this is the only readily available field
- // on the device
- + " addr=" + device.getAddress()
- + " prof=" + profile + " supprNoisy=" + suppressNoisyIntent
- + " vol=" + a2dpVolume)).printLog(TAG));
- synchronized (mDeviceStateLock) {
- if (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE,
- new BtHelper.BluetoothA2dpDeviceInfo(device))) {
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
- "A2DP connection state ignored"));
- return 0;
- }
- return mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
- device, state, profile, suppressNoisyIntent,
- AudioSystem.DEVICE_NONE, a2dpVolume);
- }
+ final BtDeviceConnectionInfo info = new BtDeviceConnectionInfo(device, state, profile,
+ suppressNoisyIntent, a2dpVolume);
+
+ // TODO add a check to try to remove unprocessed messages for the same device (the old
+ // check didn't work), and make sure it doesn't conflict with config change message
+ sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
}
/*package*/ int handleBluetoothA2dpActiveDeviceChange(
@NonNull BluetoothDevice device,
@AudioService.BtProfileConnectionState int state, int profile,
boolean suppressNoisyIntent, int a2dpVolume) {
+ // FIXME method was added by @a8439e2 but never used, and now conflicts with async behavior
+ // of handleBluetoothA2dpDeviceConfigChange and
+ // setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent
synchronized (mDeviceStateLock) {
return mDeviceInventory.handleBluetoothA2dpActiveDeviceChange(device, state, profile,
suppressNoisyIntent, a2dpVolume);
}
}
- /*package*/ int setBluetoothHearingAidDeviceConnectionState(
+ private static final class HearingAidDeviceConnectionInfo {
+ final @NonNull BluetoothDevice mDevice;
+ final @AudioService.BtProfileConnectionState int mState;
+ final boolean mSupprNoisy;
+ final int mMusicDevice;
+ final @NonNull String mEventSource;
+
+ HearingAidDeviceConnectionInfo(@NonNull BluetoothDevice device,
+ @AudioService.BtProfileConnectionState int state,
+ boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) {
+ mDevice = device;
+ mState = state;
+ mSupprNoisy = suppressNoisyIntent;
+ mMusicDevice = musicDevice;
+ mEventSource = eventSource;
+ }
+ }
+
+ /*package*/ void postBluetoothHearingAidDeviceConnectionState(
@NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) {
- AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
- "setHearingAidDeviceConnectionState state=" + state
- + " addr=" + device.getAddress()
- + " supprNoisy=" + suppressNoisyIntent
- + " src=" + eventSource)).printLog(TAG));
- synchronized (mDeviceStateLock) {
- return mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
- device, state, suppressNoisyIntent, musicDevice);
- }
+ final HearingAidDeviceConnectionInfo info = new HearingAidDeviceConnectionInfo(
+ device, state, suppressNoisyIntent, musicDevice, eventSource);
+ sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
}
// never called by system components
@@ -766,6 +787,35 @@
mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
}
break;
+ case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT: {
+ final BtDeviceConnectionInfo info = (BtDeviceConnectionInfo) msg.obj;
+ AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+ "setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent "
+ + " state=" + info.mState
+ // only querying address as this is the only readily available
+ // field on the device
+ + " addr=" + info.mDevice.getAddress()
+ + " prof=" + info.mProfile + " supprNoisy=" + info.mSupprNoisy
+ + " vol=" + info.mVolume)).printLog(TAG));
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
+ info.mDevice, info.mState, info.mProfile, info.mSupprNoisy,
+ AudioSystem.DEVICE_NONE, info.mVolume);
+ }
+ } break;
+ case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: {
+ final HearingAidDeviceConnectionInfo info =
+ (HearingAidDeviceConnectionInfo) msg.obj;
+ AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+ "setHearingAidDeviceConnectionState state=" + info.mState
+ + " addr=" + info.mDevice.getAddress()
+ + " supprNoisy=" + info.mSupprNoisy
+ + " src=" + info.mEventSource)).printLog(TAG));
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
+ info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
+ }
+ } break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);
}
@@ -809,6 +859,10 @@
private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK = 24;
private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID = 25;
private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET = 26;
+ // process external command to (dis)connect an A2DP device
+ private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT = 27;
+ // process external command to (dis)connect a hearing aid device
+ private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 28;
private static boolean isMessageHandledUnderWakelock(int msgId) {
@@ -821,6 +875,8 @@
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
case MSG_TOGGLE_HDMI:
case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
+ case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT:
+ case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT:
return true;
default:
return false;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 37f0496..41a3c98 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -578,7 +578,7 @@
return mCurAudioRoutes;
}
- /*package*/ int setBluetoothA2dpDeviceConnectionState(
+ /*package*/ void setBluetoothA2dpDeviceConnectionState(
@NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
int profile, boolean suppressNoisyIntent, int musicDevice, int a2dpVolume) {
int delay;
@@ -614,46 +614,50 @@
delay);
}
}
- return delay;
}
/*package*/ int handleBluetoothA2dpActiveDeviceChange(
@NonNull BluetoothDevice device,
@AudioService.BtProfileConnectionState int state, int profile,
boolean suppressNoisyIntent, int a2dpVolume) {
- if (state == BluetoothProfile.STATE_DISCONNECTED) {
- return setBluetoothA2dpDeviceConnectionState(device, state, profile,
- suppressNoisyIntent, AudioSystem.DEVICE_NONE, a2dpVolume);
- }
- // state == BluetoothProfile.STATE_CONNECTED
- synchronized (mConnectedDevices) {
- for (int i = 0; i < mConnectedDevices.size(); i++) {
- final DeviceInfo deviceInfo = mConnectedDevices.valueAt(i);
- if (deviceInfo.mDeviceType != AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
- continue;
- }
- // If A2DP device exists, this is either an active device change or
- // device config change
- final String existingDevicekey = mConnectedDevices.keyAt(i);
- final String deviceName = device.getName();
- final String address = device.getAddress();
- final String newDeviceKey = DeviceInfo.makeDeviceListKey(
- AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
- int a2dpCodec = mDeviceBroker.getA2dpCodec(device);
- // Device not equal to existing device, active device change
- if (!TextUtils.equals(existingDevicekey, newDeviceKey)) {
- mConnectedDevices.remove(existingDevicekey);
- mConnectedDevices.put(newDeviceKey, new DeviceInfo(
- AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, deviceName,
- address, a2dpCodec));
- mDeviceBroker.postA2dpActiveDeviceChange(
- new BtHelper.BluetoothA2dpDeviceInfo(
- device, a2dpVolume, a2dpCodec));
- return 0;
- } else {
- // Device config change for existing device
- mDeviceBroker.postBluetoothA2dpDeviceConfigChange(device);
- return 0;
+ // method was added by QC but never used, and now conflicts with async behavior of
+ // handleBluetoothA2dpDeviceConfigChange and
+ // setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent
+ if (false) {
+ if (state == BluetoothProfile.STATE_DISCONNECTED) {
+ setBluetoothA2dpDeviceConnectionState(device, state, profile,
+ suppressNoisyIntent, AudioSystem.DEVICE_NONE, a2dpVolume);
+ }
+ // state == BluetoothProfile.STATE_CONNECTED
+ synchronized (mConnectedDevices) {
+ for (int i = 0; i < mConnectedDevices.size(); i++) {
+ final DeviceInfo deviceInfo = mConnectedDevices.valueAt(i);
+ if (deviceInfo.mDeviceType != AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
+ continue;
+ }
+ // If A2DP device exists, this is either an active device change or
+ // device config change
+ final String existingDevicekey = mConnectedDevices.keyAt(i);
+ final String deviceName = device.getName();
+ final String address = device.getAddress();
+ final String newDeviceKey = DeviceInfo.makeDeviceListKey(
+ AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
+ int a2dpCodec = mDeviceBroker.getA2dpCodec(device);
+ // Device not equal to existing device, active device change
+ if (!TextUtils.equals(existingDevicekey, newDeviceKey)) {
+ mConnectedDevices.remove(existingDevicekey);
+ mConnectedDevices.put(newDeviceKey, new DeviceInfo(
+ AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, deviceName,
+ address, a2dpCodec));
+ mDeviceBroker.postA2dpActiveDeviceChange(
+ new BtHelper.BluetoothA2dpDeviceInfo(
+ device, a2dpVolume, a2dpCodec));
+ return 0;
+ } else {
+ // Device config change for existing device
+ mDeviceBroker.postBluetoothA2dpDeviceConfigChange(device);
+ return 0;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 6bd412b..91d19de 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4041,7 +4041,10 @@
@Retention(RetentionPolicy.SOURCE)
public @interface BtProfileConnectionState {}
- public int setBluetoothHearingAidDeviceConnectionState(
+ /**
+ * See AudioManager.setBluetoothHearingAidDeviceConnectionState()
+ */
+ public void setBluetoothHearingAidDeviceConnectionState(
@NonNull BluetoothDevice device, @BtProfileConnectionState int state,
boolean suppressNoisyIntent, int musicDevice)
{
@@ -4053,14 +4056,14 @@
throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
+ " (dis)connection, got " + state);
}
- return mDeviceBroker.setBluetoothHearingAidDeviceConnectionState(
+ mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
device, state, suppressNoisyIntent, musicDevice, "AudioService");
}
/**
* See AudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent()
*/
- public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
@NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
int profile, boolean suppressNoisyIntent, int a2dpVolume) {
if (device == null) {
@@ -4071,7 +4074,7 @@
throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
+ " (dis)connection, got " + state);
}
- return mDeviceBroker.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device, state,
+ mDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device, state,
profile, suppressNoisyIntent, a2dpVolume);
}
@@ -4094,6 +4097,9 @@
public int handleBluetoothA2dpActiveDeviceChange(
BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent,
int a2dpVolume) {
+ // FIXME method was added by @a8439e2 but never used, and now conflicts with async behavior
+ // of handleBluetoothA2dpDeviceConfigChange and
+ // setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent
if (device == null) {
throw new IllegalArgumentException("Illegal null device");
}
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 58c1882..04073cb 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -426,7 +426,7 @@
final BluetoothDevice btDevice = deviceList.get(0);
final @BluetoothProfile.BtProfileState int state =
mHearingAid.getConnectionState(btDevice);
- mDeviceBroker.setBluetoothHearingAidDeviceConnectionState(
+ mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
btDevice, state,
/*suppressNoisyIntent*/ false,
/*musicDevice*/ android.media.AudioSystem.DEVICE_NONE,
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index e9ae516..9882f6c 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -138,9 +138,19 @@
}
/**
- * Sets the mode, if supported.
+ * Sets the display modes the system is allowed to switch between, roughly ordered by
+ * preference.
+ *
+ * Not all display devices will automatically switch between modes, so it's important that the
+ * most-desired modes are at the beginning of the allowed array.
*/
- public void requestDisplayModesLocked(int colorMode, int modeId) {
+ public void setAllowedDisplayModesLocked(int[] modes) {
+ }
+
+ /**
+ * Sets the requested color mode.
+ */
+ public void setRequestedColorModeLocked(int colorMode) {
}
public void onOverlayChangedLocked() {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 6c00da2..32f34b8 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -167,13 +167,13 @@
private static final int MSG_DELIVER_DISPLAY_EVENT = 3;
private static final int MSG_REQUEST_TRAVERSAL = 4;
private static final int MSG_UPDATE_VIEWPORT = 5;
- private static final int MSG_REGISTER_BRIGHTNESS_TRACKER = 6;
- private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 7;
+ private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 6;
private final Context mContext;
private final DisplayManagerHandler mHandler;
private final Handler mUiHandler;
private final DisplayAdapterListener mDisplayAdapterListener;
+ private final DisplayModeDirector mDisplayModeDirector;
private WindowManagerInternal mWindowManagerInternal;
private InputManagerInternal mInputManagerInternal;
private IMediaProjectionManager mProjectionService;
@@ -310,6 +310,7 @@
mHandler = new DisplayManagerHandler(DisplayThread.get().getLooper());
mUiHandler = UiThread.getHandler();
mDisplayAdapterListener = new DisplayAdapterListener();
+ mDisplayModeDirector = new DisplayModeDirector(context, mHandler);
mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
Resources resources = mContext.getResources();
mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger(
@@ -322,7 +323,7 @@
mMinimumBrightnessCurve = new Curve(lux, nits);
mMinimumBrightnessSpline = Spline.createSpline(lux, nits);
- PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ PowerManager pm = mContext.getSystemService(PowerManager.class);
mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
mCurrentUserId = UserHandle.USER_SYSTEM;
ColorSpace[] colorSpaces = SurfaceControl.getCompositionColorSpaces();
@@ -347,9 +348,9 @@
// adapter is up so that we have it's configuration. We could load it lazily, but since
// we're going to have to read it in eventually we may as well do it here rather than after
// we've waited for the display to register itself with us.
- synchronized(mSyncRoot) {
- mPersistentDataStore.loadIfNeeded();
- loadStableDisplayValuesLocked();
+ synchronized (mSyncRoot) {
+ mPersistentDataStore.loadIfNeeded();
+ loadStableDisplayValuesLocked();
}
mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS);
@@ -417,8 +418,10 @@
mOnlyCore = onlyCore;
}
+ mDisplayModeDirector.setListener(new AllowedDisplayModeObserver());
+ mDisplayModeDirector.start();
+
mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
- mHandler.sendEmptyMessage(MSG_REGISTER_BRIGHTNESS_TRACKER);
}
@VisibleForTesting
@@ -1194,13 +1197,8 @@
requestedModeId = display.getDisplayInfoLocked().findDefaultModeByRefreshRate(
requestedRefreshRate);
}
- if (display.getRequestedModeIdLocked() != requestedModeId) {
- if (DEBUG) {
- Slog.d(TAG, "Display " + displayId + " switching to mode " + requestedModeId);
- }
- display.setRequestedModeIdLocked(requestedModeId);
- scheduleTraversalLocked(inTraversal);
- }
+ mDisplayModeDirector.getAppRequestObserver().setAppRequestedMode(
+ displayId, requestedModeId);
}
}
@@ -1319,6 +1317,28 @@
return SurfaceControl.getDisplayedContentSample(token, maxFrames, timestamp);
}
+ private void onAllowedDisplayModesChangedInternal() {
+ boolean changed = false;
+ synchronized (mSyncRoot) {
+ final int count = mLogicalDisplays.size();
+ for (int i = 0; i < count; i++) {
+ LogicalDisplay display = mLogicalDisplays.valueAt(i);
+ int displayId = mLogicalDisplays.keyAt(i);
+ int[] allowedModes = mDisplayModeDirector.getAllowedModes(displayId);
+ // Note that order is important here since not all display devices are capable of
+ // automatically switching, so we do actually want to check for equality and not
+ // just equivalent contents (regardless of order).
+ if (!Arrays.equals(allowedModes, display.getAllowedDisplayModesLocked())) {
+ display.setAllowedDisplayModesLocked(allowedModes);
+ changed = true;
+ }
+ }
+ if (changed) {
+ scheduleTraversalLocked(false);
+ }
+ }
+ }
+
private void clearViewportsLocked() {
mViewports.clear();
}
@@ -1518,6 +1538,9 @@
display.dumpLocked(ipw);
}
+ pw.println();
+ mDisplayModeDirector.dump(pw);
+
final int callbackCount = mCallbacks.size();
pw.println();
pw.println("Callbacks: size=" + callbackCount);
@@ -2431,4 +2454,10 @@
}
}
+
+ class AllowedDisplayModeObserver implements DisplayModeDirector.Listener {
+ public void onAllowedDisplayModesChanged() {
+ onAllowedDisplayModesChangedInternal();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
new file mode 100644
index 0000000..af4db07
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -0,0 +1,685 @@
+/*
+ * 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.
+ */
+
+package com.android.server.display;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import com.android.internal.R;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * The DisplayModeDirector is responsible for determining what modes are allowed to be
+ * automatically picked by the system based on system-wide and display-specific configuration.
+ */
+public class DisplayModeDirector {
+ private static final String TAG = "DisplayModeDirector";
+ private static final boolean DEBUG = false;
+
+ private static final int MSG_ALLOWED_MODES_CHANGED = 1;
+
+ // Special ID used to indicate that given vote is to be applied globally, rather than to a
+ // specific display.
+ private static final int GLOBAL_ID = -1;
+
+ // What we consider to be the system's "default" refresh rate.
+ private static final float DEFAULT_REFRESH_RATE = 60f;
+
+ // The tolerance within which we consider something approximately equals.
+ private static final float EPSILON = 0.001f;
+
+ private final Object mLock = new Object();
+ private final Context mContext;
+
+ private final DisplayModeDirectorHandler mHandler;
+
+ // A map from the display ID to the collection of votes and their priority. The latter takes
+ // the form of another map from the priority to the vote itself so that each priority is
+ // guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
+ private final SparseArray<SparseArray<Vote>> mVotesByDisplay;
+ // A map from the display ID to the supported modes on that display.
+ private final SparseArray<Display.Mode[]> mSupportedModesByDisplay;
+ // A map from the display ID to the default mode of that display.
+ private final SparseArray<Display.Mode> mDefaultModeByDisplay;
+
+ private final AppRequestObserver mAppRequestObserver;
+ private final SettingsObserver mSettingsObserver;
+ private final DisplayObserver mDisplayObserver;
+
+
+ private Listener mListener;
+
+ public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
+ mContext = context;
+ mHandler = new DisplayModeDirectorHandler(handler.getLooper());
+ mVotesByDisplay = new SparseArray<>();
+ mSupportedModesByDisplay = new SparseArray<>();
+ mDefaultModeByDisplay = new SparseArray<>();
+ mAppRequestObserver = new AppRequestObserver();
+ mSettingsObserver = new SettingsObserver(context, handler);
+ mDisplayObserver = new DisplayObserver(context, handler);
+ }
+
+ /**
+ * Tells the DisplayModeDirector to update allowed votes and begin observing relevant system
+ * state.
+ *
+ * This has to be deferred because the object may be constructed before the rest of the system
+ * is ready.
+ */
+ public void start() {
+ mSettingsObserver.observe();
+ mDisplayObserver.observe();
+ mSettingsObserver.observe();
+ synchronized (mLock) {
+ // We may have a listener already registered before the call to start, so go ahead and
+ // notify them to pick up our newly initialized state.
+ notifyAllowedModesChangedLocked();
+ }
+ }
+
+ /**
+ * Calculates the modes the system is allowed to freely switch between based on global and
+ * display-specific constraints.
+ *
+ * @param displayId The display to query for.
+ * @return The IDs of the modes the system is allowed to freely switch between.
+ */
+ @NonNull
+ public int[] getAllowedModes(int displayId) {
+ synchronized (mLock) {
+ SparseArray<Vote> votes = getVotesLocked(displayId);
+ Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
+ Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
+ if (modes == null || defaultMode == null) {
+ Slog.e(TAG, "Asked about unknown display, returning empty allowed set! (id="
+ + displayId + ")");
+ return new int[0];
+ }
+ return getAllowedModesLocked(votes, modes, defaultMode);
+ }
+ }
+
+ @NonNull
+ private SparseArray<Vote> getVotesLocked(int displayId) {
+ SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId);
+ final SparseArray<Vote> votes;
+ if (displayVotes != null) {
+ votes = displayVotes.clone();
+ } else {
+ votes = new SparseArray<>();
+ }
+
+ SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID);
+ if (globalVotes != null) {
+ for (int i = 0; i < globalVotes.size(); i++) {
+ int priority = globalVotes.keyAt(i);
+ if (votes.indexOfKey(priority) < 0) {
+ votes.put(priority, globalVotes.valueAt(i));
+ }
+ }
+ }
+ return votes;
+ }
+
+ @NonNull
+ private int[] getAllowedModesLocked(@NonNull SparseArray<Vote> votes,
+ @NonNull Display.Mode[] modes, @NonNull Display.Mode defaultMode) {
+ int lowestConsideredPriority = Vote.MIN_PRIORITY;
+ while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
+ float minRefreshRate = 0f;
+ float maxRefreshRate = Float.POSITIVE_INFINITY;
+ int height = Vote.INVALID_SIZE;
+ int width = Vote.INVALID_SIZE;
+
+ for (int priority = Vote.MAX_PRIORITY;
+ priority >= lowestConsideredPriority;
+ priority--) {
+ Vote vote = votes.get(priority);
+ if (vote == null) {
+ continue;
+ }
+ // For refresh rates, just use the tightest bounds of all the votes
+ minRefreshRate = Math.max(minRefreshRate, vote.minRefreshRate);
+ maxRefreshRate = Math.min(maxRefreshRate, vote.maxRefreshRate);
+ // For display size, use only the first vote we come across (i.e. the highest
+ // priority vote that includes the width / height).
+ if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE
+ && vote.height > 0 && vote.width > 0) {
+ width = vote.width;
+ height = vote.height;
+ }
+ }
+
+ // If we don't have anything specifying the width / height of the display, just use the
+ // default width and height. We don't want these switching out from underneath us since
+ // it's a pretty disruptive behavior.
+ if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) {
+ width = defaultMode.getPhysicalWidth();
+ height = defaultMode.getPhysicalHeight();
+ }
+
+ int[] availableModes =
+ filterModes(modes, width, height, minRefreshRate, maxRefreshRate);
+ if (availableModes.length > 0) {
+ if (DEBUG) {
+ Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes)
+ + " with lowest priority considered "
+ + Vote.priorityToString(lowestConsideredPriority)
+ + " and constraints: "
+ + "width=" + width
+ + ", height=" + height
+ + ", minRefreshRate=" + minRefreshRate
+ + ", maxRefreshRate=" + maxRefreshRate);
+ }
+ return availableModes;
+ }
+
+ if (DEBUG) {
+ Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
+ + Vote.priorityToString(lowestConsideredPriority)
+ + " and with the following constraints: "
+ + "width=" + width
+ + ", height=" + height
+ + ", minRefreshRate=" + minRefreshRate
+ + ", maxRefreshRate=" + maxRefreshRate);
+ }
+ // If we haven't found anything with the current set of votes, drop the current lowest
+ // priority vote.
+ lowestConsideredPriority++;
+ }
+
+ // If we still haven't found anything that matches our current set of votes, just fall back
+ // to the default mode.
+ return new int[] { defaultMode.getModeId() };
+ }
+
+ private int[] filterModes(Display.Mode[] supportedModes,
+ int width, int height, float minRefreshRate, float maxRefreshRate) {
+ ArrayList<Display.Mode> availableModes = new ArrayList<>();
+ for (Display.Mode mode : supportedModes) {
+ if (mode.getPhysicalWidth() != width || mode.getPhysicalHeight() != height) {
+ if (DEBUG) {
+ Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size"
+ + ": desiredWidth=" + width
+ + ": desiredHeight=" + height
+ + ": actualWidth=" + mode.getPhysicalWidth()
+ + ": actualHeight=" + mode.getPhysicalHeight());
+ }
+ continue;
+ }
+ final float refreshRate = mode.getRefreshRate();
+ // Some refresh rates are calculated based on frame timings, so they aren't *exactly*
+ // equal to expected refresh rate. Given that, we apply a bit of tolerance to this
+ // comparison.
+ if (refreshRate < (minRefreshRate - EPSILON)
+ || refreshRate > (maxRefreshRate + EPSILON)) {
+ if (DEBUG) {
+ Slog.w(TAG, "Discarding mode " + mode.getModeId()
+ + ", outside refresh rate bounds"
+ + ": minRefreshRate=" + minRefreshRate
+ + ", maxRefreshRate=" + maxRefreshRate
+ + ", modeRefreshRate=" + refreshRate);
+ }
+ continue;
+ }
+ availableModes.add(mode);
+ }
+ final int size = availableModes.size();
+ int[] availableModeIds = new int[size];
+ for (int i = 0; i < size; i++) {
+ availableModeIds[i] = availableModes.get(i).getModeId();
+ }
+ return availableModeIds;
+ }
+
+ /**
+ * Gets the observer responsible for application display mode requests.
+ */
+ @NonNull
+ public AppRequestObserver getAppRequestObserver() {
+ // We don't need to lock here because mAppRequestObserver is a final field, which is
+ // guaranteed to be visible on all threads after construction.
+ return mAppRequestObserver;
+ }
+
+ /**
+ * Sets the listener for changes to allowed display modes.
+ */
+ public void setListener(@Nullable Listener listener) {
+ synchronized (mLock) {
+ mListener = listener;
+ }
+ }
+
+ /**
+ * Print the object's state and debug information into the given stream.
+ *
+ * @param pw The stream to dump information to.
+ */
+ public void dump(PrintWriter pw) {
+ pw.println("DisplayModeDirector");
+ synchronized (mLock) {
+ pw.println(" mSupportedModesByDisplay:");
+ for (int i = 0; i < mSupportedModesByDisplay.size(); i++) {
+ final int id = mSupportedModesByDisplay.keyAt(i);
+ final Display.Mode[] modes = mSupportedModesByDisplay.valueAt(i);
+ pw.println(" " + id + " -> " + Arrays.toString(modes));
+ }
+ pw.println(" mDefaultModeByDisplay:");
+ for (int i = 0; i < mDefaultModeByDisplay.size(); i++) {
+ final int id = mDefaultModeByDisplay.keyAt(i);
+ final Display.Mode mode = mDefaultModeByDisplay.valueAt(i);
+ pw.println(" " + id + " -> " + mode);
+ }
+ pw.println(" mVotesByDisplay:");
+ for (int i = 0; i < mVotesByDisplay.size(); i++) {
+ pw.println(" " + mVotesByDisplay.keyAt(i) + ":");
+ SparseArray<Vote> votes = mVotesByDisplay.valueAt(i);
+ for (int p = Vote.MAX_PRIORITY; p >= Vote.MIN_PRIORITY; p--) {
+ Vote vote = votes.get(p);
+ if (vote == null) {
+ continue;
+ }
+ pw.println(" " + Vote.priorityToString(p) + " -> " + vote);
+ }
+ }
+ mSettingsObserver.dumpLocked(pw);
+ mAppRequestObserver.dumpLocked(pw);
+ }
+ }
+
+ private void updateVoteLocked(int priority, Vote vote) {
+ updateVoteLocked(GLOBAL_ID, priority, vote);
+ }
+
+ private void updateVoteLocked(int displayId, int priority, Vote vote) {
+ if (DEBUG) {
+ Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
+ + ", priority=" + Vote.priorityToString(priority)
+ + ", vote=" + vote + ")");
+ }
+ if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) {
+ Slog.w(TAG, "Received a vote with an invalid priority, ignoring:"
+ + " priority=" + Vote.priorityToString(priority)
+ + ", vote=" + vote, new Throwable());
+ return;
+ }
+ final SparseArray<Vote> votes = getOrCreateVotesByDisplay(displayId);
+
+ Vote currentVote = votes.get(priority);
+ if (vote != null) {
+ votes.put(priority, vote);
+ } else {
+ votes.remove(priority);
+ }
+
+ if (votes.size() == 0) {
+ if (DEBUG) {
+ Slog.i(TAG, "No votes left for display " + displayId + ", removing.");
+ }
+ mVotesByDisplay.remove(displayId);
+ }
+
+ notifyAllowedModesChangedLocked();
+ }
+
+ private void notifyAllowedModesChangedLocked() {
+ if (mListener != null && !mHandler.hasMessages(MSG_ALLOWED_MODES_CHANGED)) {
+ // We need to post this to a handler to avoid calling out while holding the lock
+ // since we know there are things that both listen for changes as well as provide
+ // information. If we did call out while holding the lock, then there's no guaranteed
+ // lock order and we run the real of risk deadlock.
+ Message msg = mHandler.obtainMessage(MSG_ALLOWED_MODES_CHANGED, mListener);
+ msg.sendToTarget();
+ }
+ }
+
+ private SparseArray<Vote> getOrCreateVotesByDisplay(int displayId) {
+ int index = mVotesByDisplay.indexOfKey(displayId);
+ if (mVotesByDisplay.indexOfKey(displayId) >= 0) {
+ return mVotesByDisplay.get(displayId);
+ } else {
+ SparseArray<Vote> votes = new SparseArray<>();
+ mVotesByDisplay.put(displayId, votes);
+ return votes;
+ }
+ }
+
+ /**
+ * Listens for changes to display mode coordination.
+ */
+ public interface Listener {
+ /**
+ * Called when the allowed display modes may have changed.
+ */
+ void onAllowedDisplayModesChanged();
+ }
+
+ private static final class DisplayModeDirectorHandler extends Handler {
+ DisplayModeDirectorHandler(Looper looper) {
+ super(looper, null, true /*async*/);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_ALLOWED_MODES_CHANGED:
+ Listener listener = (Listener) msg.obj;
+ listener.onAllowedDisplayModesChanged();
+ break;
+ }
+ }
+ }
+
+ private static final class Vote {
+ public static final int PRIORITY_USER_SETTING = 0;
+ // We split the app request into two priorities in case we can satisfy one desire without
+ // the other.
+ public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 1;
+ public static final int PRIORITY_APP_REQUEST_SIZE = 2;
+ public static final int PRIORITY_LOW_POWER_MODE = 3;
+
+ // Whenever a new priority is added, remember to update MIN_PRIORITY and/or MAX_PRIORITY as
+ // appropriate, as well as priorityToString.
+
+ public static final int MIN_PRIORITY = PRIORITY_USER_SETTING;
+ public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE;
+
+ /**
+ * A value signifying an invalid width or height in a vote.
+ */
+ public static final int INVALID_SIZE = -1;
+
+ /**
+ * The requested width of the display in pixels, or INVALID_SIZE;
+ */
+ public final int width;
+ /**
+ * The requested height of the display in pixels, or INVALID_SIZE;
+ */
+ public final int height;
+
+ /**
+ * The lowest desired refresh rate.
+ */
+ public final float minRefreshRate;
+ /**
+ * The highest desired refresh rate.
+ */
+ public final float maxRefreshRate;
+
+ public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) {
+ return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate);
+ }
+
+ public static Vote forSize(int width, int height) {
+ return new Vote(width, height, 0f, Float.POSITIVE_INFINITY);
+ }
+
+ private Vote(int width, int height,
+ float minRefreshRate, float maxRefreshRate) {
+ this.width = width;
+ this.height = height;
+ this.minRefreshRate = minRefreshRate;
+ this.maxRefreshRate = maxRefreshRate;
+ }
+
+ public static String priorityToString(int priority) {
+ switch (priority) {
+ case PRIORITY_USER_SETTING:
+ return "PRIORITY_USER_SETTING";
+ case PRIORITY_APP_REQUEST_REFRESH_RATE:
+ return "PRIORITY_APP_REQUEST_REFRESH_RATE";
+ case PRIORITY_APP_REQUEST_SIZE:
+ return "PRIORITY_APP_REQUEST_SIZE";
+ case PRIORITY_LOW_POWER_MODE:
+ return "PRIORITY_LOW_POWER_MODE";
+ default:
+ return Integer.toString(priority);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Vote{"
+ + "width=" + width
+ + ", height=" + height
+ + ", minRefreshRate=" + minRefreshRate
+ + ", maxRefreshRate=" + maxRefreshRate
+ + "}";
+ }
+ }
+
+ private final class SettingsObserver extends ContentObserver {
+ private final Uri mRefreshRateSetting =
+ Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
+ private final Uri mLowPowerModeSetting =
+ Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE);
+
+ private final Context mContext;
+ private final float mDefaultPeakRefreshRate;
+
+ SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
+ super(handler);
+ mContext = context;
+ mDefaultPeakRefreshRate = (float) context.getResources().getInteger(
+ R.integer.config_defaultPeakRefreshRate);
+ }
+
+ public void observe() {
+ final ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(mRefreshRateSetting, false /*notifyDescendants*/, this,
+ UserHandle.USER_SYSTEM);
+ cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
+ UserHandle.USER_SYSTEM);
+ synchronized (mLock) {
+ updateRefreshRateSettingLocked();
+ updateLowPowerModeSettingLocked();
+ }
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri, int userId) {
+ synchronized (mLock) {
+ if (mRefreshRateSetting.equals(uri)) {
+ updateRefreshRateSettingLocked();
+ } else if (mLowPowerModeSetting.equals(uri)) {
+ updateLowPowerModeSettingLocked();
+ }
+ }
+ }
+
+ private void updateLowPowerModeSettingLocked() {
+ boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
+ final Vote vote;
+ if (inLowPowerMode) {
+ vote = Vote.forRefreshRates(0f, 60f);
+ } else {
+ vote = null;
+ }
+ updateVoteLocked(Vote.PRIORITY_LOW_POWER_MODE, vote);
+ }
+
+ private void updateRefreshRateSettingLocked() {
+ float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
+ Settings.System.PEAK_REFRESH_RATE, DEFAULT_REFRESH_RATE);
+ Vote vote = Vote.forRefreshRates(0f, peakRefreshRate);
+ updateVoteLocked(Vote.PRIORITY_USER_SETTING, vote);
+ }
+
+ public void dumpLocked(PrintWriter pw) {
+ pw.println(" SettingsObserver");
+ pw.println(" mDefaultPeakRefreshRate: " + mDefaultPeakRefreshRate);
+ }
+ }
+
+ final class AppRequestObserver {
+ private SparseArray<Display.Mode> mAppRequestedModeByDisplay;
+
+ AppRequestObserver() {
+ mAppRequestedModeByDisplay = new SparseArray<>();
+ }
+
+ public void setAppRequestedMode(int displayId, int modeId) {
+ synchronized (mLock) {
+ setAppRequestedModeLocked(displayId, modeId);
+ }
+ }
+
+ private void setAppRequestedModeLocked(int displayId, int modeId) {
+ final Display.Mode requestedMode = findModeByIdLocked(displayId, modeId);
+ if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
+ return;
+ }
+
+ final Vote refreshRateVote;
+ final Vote sizeVote;
+ if (requestedMode != null) {
+ mAppRequestedModeByDisplay.put(displayId, requestedMode);
+ float refreshRate = requestedMode.getRefreshRate();
+ refreshRateVote = Vote.forRefreshRates(refreshRate, refreshRate);
+ sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(),
+ requestedMode.getPhysicalHeight());
+ } else {
+ mAppRequestedModeByDisplay.remove(displayId);
+ refreshRateVote = null;
+ sizeVote = null;
+ }
+ updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, refreshRateVote);
+ updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
+ return;
+ }
+
+ private Display.Mode findModeByIdLocked(int displayId, int modeId) {
+ Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
+ if (modes == null) {
+ return null;
+ }
+ for (Display.Mode mode : modes) {
+ if (mode.getModeId() == modeId) {
+ return mode;
+ }
+ }
+ return null;
+ }
+
+ public void dumpLocked(PrintWriter pw) {
+ pw.println(" AppRequestObserver");
+ pw.println(" mAppRequestedModeByDisplay:");
+ for (int i = 0; i < mAppRequestedModeByDisplay.size(); i++) {
+ final int id = mAppRequestedModeByDisplay.keyAt(i);
+ final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i);
+ pw.println(" " + id + " -> " + mode);
+ }
+ }
+ }
+
+ private final class DisplayObserver implements DisplayManager.DisplayListener {
+ // Note that we can never call into DisplayManager or any of the non-POD classes it
+ // returns, while holding mLock since it may call into DMS, which might be simultaneously
+ // calling into us already holding its own lock.
+ private final Context mContext;
+ private final Handler mHandler;
+
+ DisplayObserver(Context context, Handler handler) {
+ mContext = context;
+ mHandler = handler;
+ }
+
+ public void observe() {
+ DisplayManager dm = mContext.getSystemService(DisplayManager.class);
+ dm.registerDisplayListener(this, mHandler);
+
+ // Populate existing displays
+ SparseArray<Display.Mode[]> modes = new SparseArray<>();
+ SparseArray<Display.Mode> defaultModes = new SparseArray<>();
+ DisplayInfo info = new DisplayInfo();
+ Display[] displays = dm.getDisplays();
+ for (Display d : displays) {
+ final int displayId = d.getDisplayId();
+ d.getDisplayInfo(info);
+ modes.put(displayId, info.supportedModes);
+ defaultModes.put(displayId, info.getDefaultMode());
+ }
+ synchronized (mLock) {
+ final int size = modes.size();
+ for (int i = 0; i < size; i++) {
+ mSupportedModesByDisplay.put(modes.keyAt(i), modes.valueAt(i));
+ mDefaultModeByDisplay.put(defaultModes.keyAt(i), defaultModes.valueAt(i));
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ updateDisplayModes(displayId);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ mSupportedModesByDisplay.remove(displayId);
+ mDefaultModeByDisplay.remove(displayId);
+ }
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ updateDisplayModes(displayId);
+ }
+
+ private void updateDisplayModes(int displayId) {
+ Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
+ DisplayInfo info = new DisplayInfo();
+ d.getDisplayInfo(info);
+ boolean changed = false;
+ synchronized (mLock) {
+ if (!Arrays.equals(mSupportedModesByDisplay.get(displayId), info.supportedModes)) {
+ mSupportedModesByDisplay.put(displayId, info.supportedModes);
+ changed = true;
+ }
+ if (!Objects.equals(mDefaultModeByDisplay.get(displayId), info.getDefaultMode())) {
+ changed = true;
+ mDefaultModeByDisplay.put(displayId, info.getDefaultMode());
+ }
+ if (changed) {
+ notifyAllowedModesChangedLocked();
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 77df10b..5e5ef26 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -112,16 +112,18 @@
activeColorMode = Display.COLOR_MODE_INVALID;
}
int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken);
+ int[] allowedConfigs = SurfaceControl.getAllowedDisplayConfigs(displayToken);
LocalDisplayDevice device = mDevices.get(physicalDisplayId);
if (device == null) {
// Display was added.
final boolean isInternal = mDevices.size() == 0;
device = new LocalDisplayDevice(displayToken, physicalDisplayId,
- configs, activeConfig, colorModes, activeColorMode, isInternal);
+ configs, activeConfig, allowedConfigs, colorModes, activeColorMode,
+ isInternal);
mDevices.put(physicalDisplayId, device);
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
} else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig,
- colorModes, activeColorMode)) {
+ allowedConfigs, colorModes, activeColorMode)) {
// Display properties changed.
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
}
@@ -167,26 +169,30 @@
private boolean mHavePendingChanges;
private int mState = Display.STATE_UNKNOWN;
private int mBrightness = PowerManager.BRIGHTNESS_DEFAULT;
- private int mActivePhysIndex;
private int mDefaultModeId;
private int mActiveModeId;
private boolean mActiveModeInvalid;
+ private int[] mAllowedModeIds;
+ private boolean mAllowedModeIdsInvalid;
+ private int mActivePhysIndex;
+ private int[] mAllowedPhysIndexes;
private int mActiveColorMode;
private boolean mActiveColorModeInvalid;
private Display.HdrCapabilities mHdrCapabilities;
private boolean mSidekickActive;
private SidekickInternal mSidekickInternal;
- private SurfaceControl.PhysicalDisplayInfo mDisplayInfos[];
+ private SurfaceControl.PhysicalDisplayInfo[] mDisplayInfos;
LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo,
- int[] colorModes, int activeColorMode, boolean isInternal) {
+ int[] allowedDisplayInfos, int[] colorModes, int activeColorMode,
+ boolean isInternal) {
super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId);
mPhysicalDisplayId = physicalDisplayId;
mIsInternal = isInternal;
updatePhysicalDisplayInfoLocked(physicalDisplayInfos, activeDisplayInfo,
- colorModes, activeColorMode);
+ allowedDisplayInfos, colorModes, activeColorMode);
updateColorModesLocked(colorModes, activeColorMode);
mSidekickInternal = LocalServices.getService(SidekickInternal.class);
if (mIsInternal) {
@@ -205,9 +211,10 @@
public boolean updatePhysicalDisplayInfoLocked(
SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo,
- int[] colorModes, int activeColorMode) {
+ int[] allowedDisplayInfos, int[] colorModes, int activeColorMode) {
mDisplayInfos = Arrays.copyOf(physicalDisplayInfos, physicalDisplayInfos.length);
mActivePhysIndex = activeDisplayInfo;
+ mAllowedPhysIndexes = Arrays.copyOf(allowedDisplayInfos, allowedDisplayInfos.length);
// Build an updated list of all existing modes.
ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>();
boolean modesAdded = false;
@@ -246,8 +253,9 @@
break;
}
}
- // Check whether surface flinger spontaneously changed modes out from under us. Schedule
- // traversals to ensure that the correct state is reapplied if necessary.
+
+ // Check whether surface flinger spontaneously changed modes out from under us.
+ // Schedule traversals to ensure that the correct state is reapplied if necessary.
if (mActiveModeId != 0
&& mActiveModeId != activeRecord.mMode.getModeId()) {
mActiveModeInvalid = true;
@@ -266,6 +274,7 @@
for (DisplayModeRecord record : records) {
mSupportedModes.put(record.mMode.getModeId(), record);
}
+
// Update the default mode, if needed.
if (findDisplayInfoIndexLocked(mDefaultModeId) < 0) {
if (mDefaultModeId != 0) {
@@ -274,6 +283,7 @@
}
mDefaultModeId = activeRecord.mMode.getModeId();
}
+
// Determine whether the active mode is still there.
if (mSupportedModes.indexOfKey(mActiveModeId) < 0) {
if (mActiveModeId != 0) {
@@ -284,6 +294,21 @@
mActiveModeInvalid = true;
}
+ // Determine what the currently allowed modes are
+ mAllowedModeIds = new int[] { mActiveModeId };
+ int[] allowedModeIds = new int[mAllowedPhysIndexes.length];
+ int size = 0;
+ for (int physIndex : mAllowedPhysIndexes) {
+ int modeId = findMatchingModeIdLocked(physIndex);
+ if (modeId > 0) {
+ allowedModeIds[size++] = modeId;
+ }
+ }
+
+ // If this is different from our desired allowed modes, then mark our current set as
+ // invalid so we correct this on the next traversal.
+ mAllowedModeIdsInvalid = !Arrays.equals(allowedModeIds, mAllowedModeIds);
+
// Schedule traversals so that we apply pending changes.
sendTraversalRequestLocked();
return true;
@@ -368,11 +393,7 @@
mInfo.height = phys.height;
mInfo.modeId = mActiveModeId;
mInfo.defaultModeId = mDefaultModeId;
- mInfo.supportedModes = new Display.Mode[mSupportedModes.size()];
- for (int i = 0; i < mSupportedModes.size(); i++) {
- DisplayModeRecord record = mSupportedModes.valueAt(i);
- mInfo.supportedModes[i] = record.mMode;
- }
+ mInfo.supportedModes = getDisplayModes(mSupportedModes);
mInfo.colorMode = mActiveColorMode;
mInfo.supportedColorModes =
new int[mSupportedColorModes.size()];
@@ -593,44 +614,104 @@
}
@Override
- public void requestDisplayModesLocked(int colorMode, int modeId) {
- if (requestModeLocked(modeId) ||
- requestColorModeLocked(colorMode)) {
+ public void setRequestedColorModeLocked(int colorMode) {
+ if (requestColorModeLocked(colorMode)) {
updateDeviceInfoLocked();
}
}
@Override
+ public void setAllowedDisplayModesLocked(int[] modes) {
+ updateAllowedModesLocked(modes);
+ }
+
+ @Override
public void onOverlayChangedLocked() {
updateDeviceInfoLocked();
}
- public boolean requestModeLocked(int modeId) {
- if (modeId == 0) {
- modeId = mDefaultModeId;
- } else if (mSupportedModes.indexOfKey(modeId) < 0) {
- Slog.w(TAG, "Requested mode " + modeId + " is not supported by this display,"
- + " reverting to default display mode.");
- modeId = mDefaultModeId;
+ public void onActivePhysicalDisplayModeChangedLocked(int physIndex) {
+ if (updateActiveModeLocked(physIndex)) {
+ updateDeviceInfoLocked();
}
+ }
- int physIndex = findDisplayInfoIndexLocked(modeId);
- if (physIndex < 0) {
- Slog.w(TAG, "Requested mode ID " + modeId + " not available,"
- + " trying with default mode ID");
- modeId = mDefaultModeId;
- physIndex = findDisplayInfoIndexLocked(modeId);
- }
- if (mActivePhysIndex == physIndex) {
+ public boolean updateActiveModeLocked(int activePhysIndex) {
+ if (mActivePhysIndex == activePhysIndex) {
return false;
}
- SurfaceControl.setActiveConfig(getDisplayTokenLocked(), physIndex);
- mActivePhysIndex = physIndex;
- mActiveModeId = modeId;
- mActiveModeInvalid = false;
+ mActivePhysIndex = activePhysIndex;
+ mActiveModeId = findMatchingModeIdLocked(activePhysIndex);
+ mActiveModeInvalid = mActiveModeId == 0;
+ if (mActiveModeInvalid) {
+ Slog.w(TAG, "In unknown mode after setting allowed configs"
+ + ": allowedPhysIndexes=" + mAllowedPhysIndexes
+ + ", activePhysIndex=" + mActivePhysIndex);
+ }
return true;
}
+ public void updateAllowedModesLocked(int[] allowedModes) {
+ if (Arrays.equals(allowedModes, mAllowedModeIds) && !mAllowedModeIdsInvalid) {
+ return;
+ }
+ if (updateAllowedModesInternalLocked(allowedModes)) {
+ updateDeviceInfoLocked();
+ }
+ }
+
+ public boolean updateAllowedModesInternalLocked(int[] allowedModes) {
+ if (DEBUG) {
+ Slog.w(TAG, "updateAllowedModesInternalLocked(allowedModes="
+ + Arrays.toString(allowedModes) + ")");
+ }
+ int[] allowedPhysIndexes = new int[allowedModes.length];
+ int size = 0;
+ for (int modeId : allowedModes) {
+ int physIndex = findDisplayInfoIndexLocked(modeId);
+ if (physIndex < 0) {
+ Slog.w(TAG, "Requested mode ID " + modeId + " not available,"
+ + " dropping from allowed set.");
+ } else {
+ allowedPhysIndexes[size++] = physIndex;
+ }
+ }
+
+ // If we couldn't find one or more of the suggested allowed modes then we need to
+ // shrink the array to its actual size.
+ if (size != allowedModes.length) {
+ allowedPhysIndexes = Arrays.copyOf(allowedPhysIndexes, size);
+ }
+
+ // If we found no suitable modes, then we try again with the default mode which we
+ // assume has a suitable physical config.
+ if (size == 0) {
+ if (DEBUG) {
+ Slog.w(TAG, "No valid modes allowed, falling back to default mode (id="
+ + mDefaultModeId + ")");
+ }
+ allowedModes = new int[] { mDefaultModeId };
+ allowedPhysIndexes = new int[] { findDisplayInfoIndexLocked(mDefaultModeId) };
+ }
+
+ mAllowedModeIds = allowedModes;
+ mAllowedModeIdsInvalid = false;
+
+ if (Arrays.equals(mAllowedPhysIndexes, allowedPhysIndexes)) {
+ return false;
+ }
+ mAllowedPhysIndexes = allowedPhysIndexes;
+
+ if (DEBUG) {
+ Slog.w(TAG, "Setting allowed physical configs: allowedPhysIndexes="
+ + Arrays.toString(allowedPhysIndexes));
+ }
+
+ SurfaceControl.setAllowedDisplayConfigs(getDisplayTokenLocked(), allowedPhysIndexes);
+ int activePhysIndex = SurfaceControl.getActiveConfig(getDisplayTokenLocked());
+ return updateActiveModeLocked(activePhysIndex);
+ }
+
public boolean requestColorModeLocked(int colorMode) {
if (mActiveColorMode == colorMode) {
return false;
@@ -650,9 +731,13 @@
public void dumpLocked(PrintWriter pw) {
super.dumpLocked(pw);
pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId);
+ pw.println("mAllowedPhysIndexes=" + Arrays.toString(mAllowedPhysIndexes));
+ pw.println("mAllowedModeIds=" + Arrays.toString(mAllowedModeIds));
+ pw.println("mAllowedModeIdsInvalid=" + mAllowedModeIdsInvalid);
pw.println("mActivePhysIndex=" + mActivePhysIndex);
pw.println("mActiveModeId=" + mActiveModeId);
pw.println("mActiveColorMode=" + mActiveColorMode);
+ pw.println("mDefaultModeId=" + mDefaultModeId);
pw.println("mState=" + Display.stateToString(mState));
pw.println("mBrightness=" + mBrightness);
pw.println("mBacklight=" + mBacklight);
@@ -687,10 +772,31 @@
return -1;
}
+ private int findMatchingModeIdLocked(int physIndex) {
+ SurfaceControl.PhysicalDisplayInfo info = mDisplayInfos[physIndex];
+ for (int i = 0; i < mSupportedModes.size(); i++) {
+ DisplayModeRecord record = mSupportedModes.valueAt(i);
+ if (record.hasMatchingMode(info)) {
+ return record.mMode.getModeId();
+ }
+ }
+ return 0;
+ }
+
private void updateDeviceInfoLocked() {
mInfo = null;
sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
}
+
+ private Display.Mode[] getDisplayModes(SparseArray<DisplayModeRecord> records) {
+ final int size = records.size();
+ Display.Mode[] modes = new Display.Mode[size];
+ for (int i = 0; i < size; i++) {
+ DisplayModeRecord record = records.valueAt(i);
+ modes[i] = record.mMode;
+ }
+ return modes;
+ }
}
/** Supplies a context whose Resources apply runtime-overlays */
@@ -745,12 +851,23 @@
}
@Override
- public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+ public void onConfigChanged(long timestampNanos, long physicalDisplayId, int physIndex) {
if (DEBUG) {
Slog.d(TAG, "onConfigChanged("
+ "timestampNanos=" + timestampNanos
- + ", builtInDisplayId=" + physicalDisplayId
- + ", configId=" + configId + ")");
+ + ", physicalDisplayId=" + physicalDisplayId
+ + ", physIndex=" + physIndex + ")");
+ }
+ synchronized (getSyncRoot()) {
+ LocalDisplayDevice device = mDevices.get(physicalDisplayId);
+ if (device == null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received config change for unhandled physical display: "
+ + "physicalDisplayId=" + physicalDisplayId);
+ }
+ return;
+ }
+ device.onActivePhysicalDisplayModeChangedLocked(physIndex);
}
}
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index b21f0a7..a7b9051 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -87,7 +87,7 @@
// True if the logical display has unique content.
private boolean mHasContent;
- private int mRequestedModeId;
+ private int[] mAllowedDisplayModes = new int[0];
private int mRequestedColorMode;
// The display offsets to apply to the display projection.
@@ -354,12 +354,14 @@
// Set the layer stack.
device.setLayerStackLocked(t, isBlanked ? BLANK_LAYER_STACK : mLayerStack);
- // Set the color mode and mode.
+ // Set the color mode and allowed display mode.
if (device == mPrimaryDisplayDevice) {
- device.requestDisplayModesLocked(
- mRequestedColorMode, mRequestedModeId);
+ device.setAllowedDisplayModesLocked(mAllowedDisplayModes);
+ device.setRequestedColorModeLocked(mRequestedColorMode);
} else {
- device.requestDisplayModesLocked(0, 0); // Revert to default.
+ // Reset to default for non primary displays
+ device.setAllowedDisplayModesLocked(new int[] {0});
+ device.setRequestedColorModeLocked(0);
}
// Only grab the display info now as it may have been changed based on the requests above.
@@ -463,17 +465,17 @@
}
/**
- * Requests the given mode.
+ * Sets the display modes the system is free to switch between.
*/
- public void setRequestedModeIdLocked(int modeId) {
- mRequestedModeId = modeId;
+ public void setAllowedDisplayModesLocked(int[] modes) {
+ mAllowedDisplayModes = modes;
}
/**
- * Returns the pending requested mode.
+ * Returns the display modes the system is free to switch between.
*/
- public int getRequestedModeIdLocked() {
- return mRequestedModeId;
+ public int[] getAllowedDisplayModesLocked() {
+ return mAllowedDisplayModes;
}
/**
@@ -532,7 +534,7 @@
pw.println("mDisplayId=" + mDisplayId);
pw.println("mLayerStack=" + mLayerStack);
pw.println("mHasContent=" + mHasContent);
- pw.println("mRequestedMode=" + mRequestedModeId);
+ pw.println("mAllowedDisplayModes=" + Arrays.toString(mAllowedDisplayModes));
pw.println("mRequestedColorMode=" + mRequestedColorMode);
pw.println("mDisplayOffset=(" + mDisplayOffsetX + ", " + mDisplayOffsetY + ")");
pw.println("mDisplayScalingDisabled=" + mDisplayScalingDisabled);
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index 2f507d1..60cfbd0 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -16,9 +16,6 @@
package com.android.server.display;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.IndentingPrintWriter;
-
import android.content.Context;
import android.database.ContentObserver;
import android.graphics.SurfaceTexture;
@@ -32,6 +29,9 @@
import android.view.Surface;
import android.view.SurfaceControl;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -315,7 +315,16 @@
}
@Override
- public void requestDisplayModesLocked(int color, int id) {
+ public void setAllowedDisplayModesLocked(int[] modes) {
+ final int id;
+ if (modes.length > 0) {
+ // The allowed modes should be ordered by preference, so just use the first mode
+ // here.
+ id = modes[0];
+ } else {
+ // If we don't have any allowed modes, just use the default mode.
+ id = 0;
+ }
int index = -1;
if (id == 0) {
// Use the default.
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 9ab9975..71ec5b0 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -142,7 +142,6 @@
// these need to match ElapsedRealtimeFlags enum in types.hal
private static final int ELAPSED_REALTIME_HAS_TIMESTAMP_NS = 1;
- private static final int ELAPSED_REALTIME_HAS_TIME_UNCERTAINTY_NS = 2;
// IMPORTANT - the GPS_DELETE_* symbols here must match GnssAidingData enum in IGnss.hal
private static final int GPS_DELETE_EPHEMERIS = 0x0001;
@@ -769,18 +768,15 @@
float bearingAccuracyDegrees = location.getBearingAccuracyDegrees();
long timestamp = location.getTime();
- int elapsedRealtimeFlags = ELAPSED_REALTIME_HAS_TIMESTAMP_NS
- | (location.hasElapsedRealtimeUncertaintyNanos()
- ? ELAPSED_REALTIME_HAS_TIME_UNCERTAINTY_NS : 0);
+ int elapsedRealtimeFlags = ELAPSED_REALTIME_HAS_TIMESTAMP_NS;
long elapsedRealtimeNanos = location.getElapsedRealtimeNanos();
- long elapsedRealtimeUncertaintyNanos = location.getElapsedRealtimeUncertaintyNanos();
native_inject_best_location(
gnssLocationFlags, latitudeDegrees, longitudeDegrees,
altitudeMeters, speedMetersPerSec, bearingDegrees,
horizontalAccuracyMeters, verticalAccuracyMeters,
speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
- elapsedRealtimeFlags, elapsedRealtimeNanos, elapsedRealtimeUncertaintyNanos);
+ elapsedRealtimeFlags, elapsedRealtimeNanos);
}
/** Returns true if the location request is too frequent. */
@@ -2174,8 +2170,7 @@
double altitudeMeters, float speedMetersPerSec, float bearingDegrees,
float horizontalAccuracyMeters, float verticalAccuracyMeters,
float speedAccuracyMetersPerSecond, float bearingAccuracyDegrees,
- long timestamp, int elapsedRealtimeFlags, long elapsedRealtimeNanos,
- long elapsedRealtimeUncertaintyNanos);
+ long timestamp, int elapsedRealtimeFlags, long elapsedRealtimeNanos);
private native void native_inject_location(double latitude, double longitude, float accuracy);
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index e8882ec..d39f20c 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -491,10 +491,6 @@
SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs);
}
- if (flags & ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS) {
- SET(ElapsedUncertaintyRealtimeNanos, location.elapsedRealtime.timeUncertaintyNs);
- }
-
return object.get();
}
@@ -525,8 +521,7 @@
jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees,
jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
- jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
- jlong elapsedRealtimeUncertaintyNanos) {
+ jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos) {
GnssLocation_V2_0 location;
location.v1_0 = createGnssLocation_V1_0(
gnssLocationFlags, latitudeDegrees, longitudeDegrees, altitudeMeters,
@@ -536,7 +531,6 @@
location.elapsedRealtime.flags = static_cast<uint16_t>(elapsedRealtimeFlags);
location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos);
- location.elapsedRealtime.timeUncertaintyNs = static_cast<uint64_t>(elapsedRealtimeUncertaintyNanos);
return location;
}
@@ -1893,8 +1887,7 @@
jfloat bearingAccuracyDegrees,
jlong timestamp,
jint elapsedRealtimeFlags,
- jlong elapsedRealtimeNanos,
- jlong elapsedRealtimeUncertaintyNanos) {
+ jlong elapsedRealtimeNanos) {
if (gnssHal_V2_0 != nullptr) {
GnssLocation_V2_0 location = createGnssLocation_V2_0(
gnssLocationFlags,
@@ -1909,8 +1902,7 @@
bearingAccuracyDegrees,
timestamp,
elapsedRealtimeFlags,
- elapsedRealtimeNanos,
- elapsedRealtimeUncertaintyNanos);
+ elapsedRealtimeNanos);
auto result = gnssHal_V2_0->injectBestLocation_2_0(location);
if (!result.isOk() || !result) {
@@ -2821,7 +2813,7 @@
android_location_GnssLocationProvider_read_nmea)},
{"native_inject_time", "(JJI)V", reinterpret_cast<void *>(
android_location_GnssLocationProvider_inject_time)},
- {"native_inject_best_location", "(IDDDFFFFFFJIJJ)V", reinterpret_cast<void *>(
+ {"native_inject_best_location", "(IDDDFFFFFFJIJ)V", reinterpret_cast<void *>(
android_location_GnssLocationProvider_inject_best_location)},
{"native_inject_location", "(DDF)V", reinterpret_cast<void *>(
android_location_GnssLocationProvider_inject_location)},
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
index 8caa39d..1f86171 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
@@ -175,9 +175,9 @@
/** Verify app usage limit observer is added */
@Test
public void testAppUsageLimitObserver_AddObserver() {
- addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID1));
- addAppUsageLimitObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN, TIME_30_MIN);
assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID2));
assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID1));
}
@@ -203,7 +203,7 @@
/** Verify app usage limit observer is removed */
@Test
public void testAppUsageLimitObserver_RemoveObserver() {
- addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID1));
mController.removeAppUsageLimitObserver(UID, OBS_ID1, USER_ID);
assertFalse("Observer wasn't removed", hasAppUsageLimitObserver(UID, OBS_ID1));
@@ -290,9 +290,9 @@
/** Re-adding an observer should result in only one copy */
@Test
public void testAppUsageLimitObserver_ObserverReAdd() {
- addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID1));
- addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_10_MIN);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_10_MIN, TIME_10_MIN);
assertTrue("Observer wasn't added",
getAppUsageLimitObserver(UID, OBS_ID1).getTimeLimitMs() == TIME_10_MIN);
mController.removeAppUsageLimitObserver(UID, OBS_ID1, USER_ID);
@@ -304,7 +304,7 @@
public void testAllObservers_ExclusiveObserverIds() {
addAppUsageObserver(OBS_ID1, GROUP1, TIME_10_MIN);
addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
- addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_10_MIN);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_10_MIN, TIME_10_MIN);
assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID1));
@@ -396,7 +396,7 @@
@Test
public void testAppUsageLimitObserver_Accumulation() throws Exception {
setTime(0L);
- addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
startUsage(PKG_SOC1);
// Add 10 mins
setTime(TIME_10_MIN);
@@ -456,7 +456,7 @@
@Test
public void testAppUsageLimitObserver_TimeoutOtherApp() throws Exception {
setTime(0L);
- addAppUsageLimitObserver(OBS_ID1, GROUP1, 4_000L);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, 4_000L, 4_000L);
startUsage(PKG_SOC2);
assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
setTime(6_000L);
@@ -498,7 +498,7 @@
@Test
public void testAppUsageLimitObserver_Timeout() throws Exception {
setTime(0L);
- addAppUsageLimitObserver(OBS_ID1, GROUP1, 4_000L);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, 4_000L, 4_000L);
startUsage(PKG_SOC1);
setTime(6_000L);
assertTrue(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
@@ -551,7 +551,7 @@
setTime(TIME_10_MIN);
startUsage(PKG_GAME1);
setTime(TIME_30_MIN);
- addAppUsageLimitObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN, TIME_30_MIN);
setTime(TIME_30_MIN + TIME_10_MIN);
stopUsage(PKG_GAME1);
assertFalse(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
@@ -612,7 +612,7 @@
startUsage(PKG_SOC1);
setTime(TIME_10_MIN);
// 10 second time limit
- addAppUsageLimitObserver(OBS_ID1, GROUP_SOC, 10_000L);
+ addAppUsageLimitObserver(OBS_ID1, GROUP_SOC, 10_000L, 10_000L);
setTime(TIME_10_MIN + 5_000L);
// Shouldn't call back in 6 seconds
assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
@@ -692,23 +692,23 @@
public void testAppUsageLimitObserver_MaxObserverLimit() throws Exception {
boolean receivedException = false;
int ANOTHER_UID = UID + 1;
- addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
- addAppUsageLimitObserver(OBS_ID2, GROUP1, TIME_30_MIN);
- addAppUsageLimitObserver(OBS_ID3, GROUP1, TIME_30_MIN);
- addAppUsageLimitObserver(OBS_ID4, GROUP1, TIME_30_MIN);
- addAppUsageLimitObserver(OBS_ID5, GROUP1, TIME_30_MIN);
- addAppUsageLimitObserver(OBS_ID6, GROUP1, TIME_30_MIN);
- addAppUsageLimitObserver(OBS_ID7, GROUP1, TIME_30_MIN);
- addAppUsageLimitObserver(OBS_ID8, GROUP1, TIME_30_MIN);
- addAppUsageLimitObserver(OBS_ID9, GROUP1, TIME_30_MIN);
- addAppUsageLimitObserver(OBS_ID10, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID2, GROUP1, TIME_30_MIN, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID3, GROUP1, TIME_30_MIN, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID4, GROUP1, TIME_30_MIN, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID5, GROUP1, TIME_30_MIN, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID6, GROUP1, TIME_30_MIN, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID7, GROUP1, TIME_30_MIN, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID8, GROUP1, TIME_30_MIN, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID9, GROUP1, TIME_30_MIN, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID10, GROUP1, TIME_30_MIN, TIME_30_MIN);
// Readding an observer should not cause an IllegalStateException
- addAppUsageLimitObserver(OBS_ID5, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID5, GROUP1, TIME_30_MIN, TIME_30_MIN);
// Adding an observer for a different uid shouldn't cause an IllegalStateException
mController.addAppUsageLimitObserver(
- ANOTHER_UID, OBS_ID11, GROUP1, TIME_30_MIN, null, USER_ID);
+ ANOTHER_UID, OBS_ID11, GROUP1, TIME_30_MIN, TIME_30_MIN, null, USER_ID);
try {
- addAppUsageLimitObserver(OBS_ID11, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID11, GROUP1, TIME_30_MIN, TIME_30_MIN);
} catch (IllegalStateException ise) {
receivedException = true;
}
@@ -748,9 +748,9 @@
public void testAppUsageLimitObserver_MinimumTimeLimit() throws Exception {
boolean receivedException = false;
// adding an observer with a one minute time limit should not cause an exception
- addAppUsageLimitObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT, MIN_TIME_LIMIT);
try {
- addAppUsageLimitObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT - 1);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT - 1, MIN_TIME_LIMIT - 1);
} catch (IllegalArgumentException iae) {
receivedException = true;
}
@@ -807,7 +807,7 @@
@Test
public void testAppUsageLimitObserver_ConcurrentUsage() throws Exception {
setTime(0L);
- addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
AppTimeLimitController.UsageGroup group = getAppUsageLimitObserver(UID, OBS_ID1);
startUsage(PKG_SOC1);
// Add 10 mins
@@ -967,7 +967,7 @@
/** Verify app usage limit observer added correctly reports its total usage limit */
@Test
public void testAppUsageLimitObserver_GetTotalUsageLimit() {
- addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
AppTimeLimitController.AppUsageLimitGroup group = getAppUsageLimitObserver(UID, OBS_ID1);
assertNotNull("Observer wasn't added", group);
assertEquals("Observer didn't correctly report total usage limit",
@@ -978,7 +978,7 @@
@Test
public void testAppUsageLimitObserver_GetUsageRemaining() {
setTime(0L);
- addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
startUsage(PKG_SOC1);
setTime(TIME_10_MIN);
stopUsage(PKG_SOC1);
@@ -993,8 +993,8 @@
*/
@Test
public void testAppUsageLimitObserver_GetAppUsageLimit() {
- addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
- addAppUsageLimitObserver(OBS_ID2, GROUP_SOC, TIME_10_MIN);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID2, GROUP_SOC, TIME_10_MIN, TIME_10_MIN);
UsageStatsManagerInternal.AppUsageLimitData group = getAppUsageLimit(PKG_SOC1);
assertEquals("Observer with the smallest usage limit remaining wasn't returned",
TIME_10_MIN, group.getTotalUsageLimit());
@@ -1006,8 +1006,8 @@
@Test
public void testAppUsageLimitObserver_GetAppUsageLimitUsed() {
setTime(0L);
- addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
- addAppUsageLimitObserver(OBS_ID2, GROUP_SOC, TIME_10_MIN);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID2, GROUP_SOC, TIME_10_MIN, TIME_10_MIN);
startUsage(PKG_GAME1);
setTime(TIME_10_MIN * 2 + TIME_1_MIN);
stopUsage(PKG_GAME1);
@@ -1024,8 +1024,8 @@
@Test
public void testAppUsageLimitObserver_GetAppUsageLimitAllUsed() {
setTime(0L);
- addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
- addAppUsageLimitObserver(OBS_ID2, GROUP_SOC, TIME_10_MIN);
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
+ addAppUsageLimitObserver(OBS_ID2, GROUP_SOC, TIME_10_MIN, TIME_10_MIN);
startUsage(PKG_SOC1);
setTime(TIME_10_MIN);
stopUsage(PKG_SOC1);
@@ -1035,10 +1035,21 @@
0L, group.getUsageRemaining());
}
+ /** Verify that a limit of 0 is not allowed. */
+ @Test
+ public void testAppUsageLimitObserver_ZeroTimeLimitIsNotAllowed() {
+ try {
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, 0, 0);
+ fail("timeLimit of 0 should not be allowed.");
+ } catch (IllegalArgumentException expected) {
+ // Exception expected.
+ }
+ }
+
/** Verify that a limit of 0 is allowed for the special case of re-registering an observer. */
@Test
- public void testAppUsageLimitObserver_ZeroTimeLimitIsAllowed() {
- addAppUsageLimitObserver(OBS_ID1, GROUP1, 0);
+ public void testAppUsageLimitObserver_ZeroTimeRemainingIsAllowed() {
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_1_MIN, 0);
AppTimeLimitController.AppUsageLimitGroup group = getAppUsageLimitObserver(UID, OBS_ID1);
assertNotNull("Observer wasn't added", group);
assertEquals("Usage remaining was not 0.", 0, group.getUsageRemaining());
@@ -1066,8 +1077,10 @@
null, null, USER_ID);
}
- private void addAppUsageLimitObserver(int observerId, String[] packages, long timeLimit) {
- mController.addAppUsageLimitObserver(UID, observerId, packages, timeLimit, null, USER_ID);
+ private void addAppUsageLimitObserver(int observerId, String[] packages, long timeLimit,
+ long timeRemaining) {
+ mController.addAppUsageLimitObserver(UID, observerId, packages, timeLimit, timeRemaining,
+ null, USER_ID);
}
/** Is there still an app usage observer by that id */
diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
index 731cbf4..f3d6387 100644
--- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java
+++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
@@ -511,8 +511,10 @@
class AppUsageLimitGroup extends UsageGroup {
public AppUsageLimitGroup(UserData user, ObserverAppData observerApp, int observerId,
- String[] observed, long timeLimitMs, PendingIntent limitReachedCallback) {
+ String[] observed, long timeLimitMs, long timeRemainingMs,
+ PendingIntent limitReachedCallback) {
super(user, observerApp, observerId, observed, timeLimitMs, limitReachedCallback);
+ mUsageTimeMs = timeLimitMs - timeRemainingMs;
}
@Override
@@ -839,9 +841,9 @@
* Existing app usage limit observer with the same observerId will be removed.
*/
public void addAppUsageLimitObserver(int requestingUid, int observerId, String[] observed,
- long timeLimit, PendingIntent callbackIntent, @UserIdInt int userId) {
- // Allow the special case of the limit being 0, but with no callback.
- if (timeLimit != 0L && timeLimit < getMinTimeLimit()) {
+ long timeLimit, long timeRemaining, PendingIntent callbackIntent,
+ @UserIdInt int userId) {
+ if (timeLimit < getMinTimeLimit()) {
throw new IllegalArgumentException("Time limit must be >= " + getMinTimeLimit());
}
synchronized (mLock) {
@@ -859,7 +861,7 @@
"Too many app usage observers added by uid " + requestingUid);
}
group = new AppUsageLimitGroup(user, observerApp, observerId, observed, timeLimit,
- timeLimit == 0L ? null : callbackIntent);
+ timeRemaining, timeRemaining == 0L ? null : callbackIntent);
observerApp.appUsageLimitGroups.append(observerId, group);
if (DEBUG) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index b14d722..27fdbcb 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1399,7 +1399,8 @@
@Override
public void registerAppUsageLimitObserver(int observerId, String[] packages,
- long timeLimitMs, PendingIntent callbackIntent, String callingPackage) {
+ long timeLimitMs, long timeRemainingMs, PendingIntent callbackIntent,
+ String callingPackage) {
if (!hasPermissions(callingPackage,
Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)) {
throw new SecurityException("Caller doesn't have both SUSPEND_APPS and "
@@ -1409,7 +1410,11 @@
if (packages == null || packages.length == 0) {
throw new IllegalArgumentException("Must specify at least one package");
}
- if (callbackIntent == null && timeLimitMs != 0L) {
+ if (timeRemainingMs > timeLimitMs) {
+ throw new IllegalArgumentException(
+ "Remaining time can't be greater than total time.");
+ }
+ if (callbackIntent == null && timeRemainingMs != 0L) {
throw new NullPointerException("callbackIntent can't be null");
}
final int callingUid = Binder.getCallingUid();
@@ -1417,7 +1422,7 @@
final long token = Binder.clearCallingIdentity();
try {
UsageStatsService.this.registerAppUsageLimitObserver(callingUid, observerId,
- packages, timeLimitMs, callbackIntent, userId);
+ packages, timeLimitMs, timeRemainingMs, callbackIntent, userId);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1545,9 +1550,9 @@
}
void registerAppUsageLimitObserver(int callingUid, int observerId, String[] packages,
- long timeLimitMs, PendingIntent callbackIntent, int userId) {
- mAppTimeLimit.addAppUsageLimitObserver(callingUid, observerId, packages, timeLimitMs,
- callbackIntent, userId);
+ long timeLimitMs, long timeRemainingMs, PendingIntent callbackIntent, int userId) {
+ mAppTimeLimit.addAppUsageLimitObserver(callingUid, observerId, packages,
+ timeLimitMs, timeRemainingMs, callbackIntent, userId);
}
void unregisterAppUsageLimitObserver(int callingUid, int observerId, int userId) {
diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java
index 368f0c6..3a34005 100644
--- a/telephony/java/android/telephony/CallAttributes.java
+++ b/telephony/java/android/telephony/CallAttributes.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -29,15 +30,15 @@
* @hide
*/
@SystemApi
-public class CallAttributes implements Parcelable {
+public final class CallAttributes implements Parcelable {
private PreciseCallState mPreciseCallState;
@NetworkType
private int mNetworkType; // TelephonyManager.NETWORK_TYPE_* ints
private CallQuality mCallQuality;
- public CallAttributes(PreciseCallState state, @NetworkType int networkType,
- CallQuality callQuality) {
+ public CallAttributes(@NonNull PreciseCallState state, @NetworkType int networkType,
+ @NonNull CallQuality callQuality) {
this.mPreciseCallState = state;
this.mNetworkType = networkType;
this.mCallQuality = callQuality;
@@ -59,6 +60,7 @@
/**
* Returns the {@link PreciseCallState} of the call.
*/
+ @NonNull
public PreciseCallState getPreciseCallState() {
return mPreciseCallState;
}
@@ -96,6 +98,7 @@
/**
* Returns the {#link CallQuality} of the call.
*/
+ @NonNull
public CallQuality getCallQuality() {
return mCallQuality;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index cf2b1ea..aaf5f33 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1566,6 +1566,7 @@
* Returns the Type Allocation Code from the IMEI. Return null if Type Allocation Code is not
* available.
*/
+ @Nullable
public String getTypeAllocationCode() {
return getTypeAllocationCode(getSlotIndex());
}
@@ -1576,6 +1577,7 @@
*
* @param slotIndex of which Type Allocation Code is returned
*/
+ @Nullable
public String getTypeAllocationCode(int slotIndex) {
ITelephony telephony = getITelephony();
if (telephony == null) return null;
@@ -1636,6 +1638,7 @@
* Returns the Manufacturer Code from the MEID. Return null if Manufacturer Code is not
* available.
*/
+ @Nullable
public String getManufacturerCode() {
return getManufacturerCode(getSlotIndex());
}
@@ -1646,6 +1649,7 @@
*
* @param slotIndex of which Type Allocation Code is returned
*/
+ @Nullable
public String getManufacturerCode(int slotIndex) {
ITelephony telephony = getITelephony();
if (telephony == null) return null;
@@ -6005,6 +6009,7 @@
* @return IMS Service Table or null if not present or not loaded
* @hide
*/
+ @Nullable
@SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public String getIsimIst() {
@@ -10367,12 +10372,6 @@
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void switchMultiSimConfig(int numOfSims) {
- //only proceed if multi-sim is not restricted
- if (!isMultisimSupported()) {
- Rlog.e(TAG, "switchMultiSimConfig not possible. It is restricted or not supported.");
- return;
- }
-
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index ad34349..afbf46d 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -17,6 +17,7 @@
import android.Manifest;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
@@ -429,6 +430,7 @@
*
* @return an EuiccManager that uses the given card ID for all calls.
*/
+ @NonNull
public EuiccManager createForCardId(int cardId) {
return new EuiccManager(mContext, cardId);
}
diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
index 337375a..a09844d 100644
--- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java
+++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
@@ -16,6 +16,7 @@
package android.telephony.ims;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.RemoteException;
import android.telephony.CallQuality;
@@ -606,7 +607,7 @@
*
* @param profile updated ImsStreamMediaProfile
*/
- public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
+ public void callSessionRttAudioIndicatorChanged(@NonNull ImsStreamMediaProfile profile) {
try {
mListener.callSessionRttAudioIndicatorChanged(profile);
} catch (RemoteException e) {
@@ -619,7 +620,7 @@
*
* @param callQuality The new call quality
*/
- public void callQualityChanged(CallQuality callQuality) {
+ public void callQualityChanged(@NonNull CallQuality callQuality) {
try {
mListener.callQualityChanged(callQuality);
} catch (RemoteException e) {
diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java
index 10001bc..464db34 100644
--- a/telephony/java/android/telephony/ims/ImsSsData.java
+++ b/telephony/java/android/telephony/ims/ImsSsData.java
@@ -17,6 +17,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,6 +25,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
/**
* Provides STK Call Control Supplementary Service information.
@@ -260,13 +264,13 @@
public final int result;
private int[] mSsInfo;
- private ImsCallForwardInfo[] mCfInfo;
- private ImsSsInfo[] mImsSsInfo;
+ private List<ImsCallForwardInfo> mCfInfo;
+ private List<ImsSsInfo> mImsSsInfo;
/**
* Builder for optional ImsSsData parameters.
*/
- public static class Builder {
+ public static final class Builder {
private ImsSsData mImsSsData;
/**
@@ -301,7 +305,7 @@
* Set the array of {@link ImsSsInfo}s that are associated with this supplementary service
* data.
*/
- public @NonNull Builder setSuppServiceInfo(@NonNull ImsSsInfo[] imsSsInfos) {
+ public @NonNull Builder setSuppServiceInfo(@NonNull List<ImsSsInfo> imsSsInfos) {
mImsSsData.mImsSsInfo = imsSsInfos;
return this;
}
@@ -311,7 +315,7 @@
* service data.
*/
public @NonNull Builder setCallForwardingInfo(
- @NonNull ImsCallForwardInfo[] imsCallForwardInfos) {
+ @NonNull List<ImsCallForwardInfo> imsCallForwardInfos) {
mImsSsData.mCfInfo = imsCallForwardInfos;
return this;
}
@@ -360,8 +364,8 @@
serviceClass = in.readInt();
result = in.readInt();
mSsInfo = in.createIntArray();
- mCfInfo = (ImsCallForwardInfo[])in.readParcelableArray(this.getClass().getClassLoader());
- mImsSsInfo = (ImsSsInfo[])in.readParcelableArray(this.getClass().getClassLoader());
+ mCfInfo = in.readParcelableList(new ArrayList<>(), this.getClass().getClassLoader());
+ mImsSsInfo = in.readParcelableList(new ArrayList<>(), this.getClass().getClassLoader());
}
public static final @android.annotation.NonNull Creator<ImsSsData> CREATOR = new Creator<ImsSsData>() {
@@ -384,8 +388,8 @@
out.writeInt(getServiceClass());
out.writeInt(getResult());
out.writeIntArray(mSsInfo);
- out.writeParcelableArray(mCfInfo, 0);
- out.writeParcelableArray(mImsSsInfo, 0);
+ out.writeParcelableList(mCfInfo, 0);
+ out.writeParcelableList(mImsSsInfo, 0);
}
@Override
@@ -500,12 +504,12 @@
/** @hide */
public void setImsSpecificSuppServiceInfo(ImsSsInfo[] imsSsInfo) {
- mImsSsInfo = imsSsInfo;
+ mImsSsInfo = Arrays.asList(imsSsInfo);
}
/** @hide */
public void setCallForwardingInfo(ImsCallForwardInfo[] cfInfo) {
- mCfInfo = cfInfo;
+ mCfInfo = Arrays.asList(cfInfo);
}
/**
@@ -524,7 +528,7 @@
int[] result = new int[2];
- if (mImsSsInfo == null || mImsSsInfo.length == 0) {
+ if (mImsSsInfo == null || mImsSsInfo.size() == 0) {
Rlog.e(TAG, "getSuppServiceInfoCompat: Could not parse mImsSsInfo, returning empty "
+ "int[]");
return result;
@@ -535,26 +539,26 @@
if (isTypeClir()) {
// Assume there will only be one ImsSsInfo.
// contains {"n","m"} parameters
- result[0] = mImsSsInfo[0].getClirOutgoingState();
- result[1] = mImsSsInfo[0].getClirInterrogationStatus();
+ result[0] = mImsSsInfo.get(0).getClirOutgoingState();
+ result[1] = mImsSsInfo.get(0).getClirInterrogationStatus();
return result;
}
// COLR 7.31
if (isTypeColr()) {
- result[0] = mImsSsInfo[0].getProvisionStatus();
+ result[0] = mImsSsInfo.get(0).getProvisionStatus();
}
// Facility Lock CLCK 7.4 (for call barring), CLIP 7.6, COLP 7.8, as well as any
// other result, just return the status for the "n" parameter and provisioning status for
// "m" as the default.
- result[0] = mImsSsInfo[0].getStatus();
- result[1] = mImsSsInfo[0].getProvisionStatus();
+ result[0] = mImsSsInfo.get(0).getStatus();
+ result[1] = mImsSsInfo.get(0).getProvisionStatus();
return result;
}
/**
* @return an array of {@link ImsSsInfo}s associated with this supplementary service data.
*/
- public @NonNull ImsSsInfo[] getSuppServiceInfo() {
+ public @NonNull List<ImsSsInfo> getSuppServiceInfo() {
return mImsSsInfo;
}
@@ -562,7 +566,7 @@
* @return an array of {@link ImsCallForwardInfo}s associated with this supplementary service
* data.
**/
- public ImsCallForwardInfo[] getCallForwardInfo() {
+ public @Nullable List<ImsCallForwardInfo> getCallForwardInfo() {
return mCfInfo;
}
diff --git a/telephony/java/android/telephony/ims/ImsSsInfo.java b/telephony/java/android/telephony/ims/ImsSsInfo.java
index 303a9fe..18e7530 100644
--- a/telephony/java/android/telephony/ims/ImsSsInfo.java
+++ b/telephony/java/android/telephony/ims/ImsSsInfo.java
@@ -173,7 +173,7 @@
/**
* Builds {@link ImsSsInfo} instances, which may include optional parameters.
*/
- public static class Builder {
+ public static final class Builder {
private final ImsSsInfo mImsSsInfo;
@@ -304,7 +304,7 @@
/**
* @return The Incoming Communication Barring (ICB) number.
*/
- public String getIncomingCommunicationBarringNumber() {
+ public @Nullable String getIncomingCommunicationBarringNumber() {
return mIcbNum;
}
diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
index 0105893..adcd11a 100644
--- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
+++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
@@ -41,6 +41,7 @@
import android.widget.TextView;
import android.widget.Toast;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -196,8 +197,8 @@
intent.setPackage(getPackageName());
intent.putExtra(EXTRA_KEY_TIMEOUT, true);
mUsageStatsManager.registerAppUsageLimitObserver(1, packages,
- 60, TimeUnit.SECONDS, PendingIntent.getActivity(UsageStatsActivity.this,
- 1, intent, 0));
+ Duration.ofSeconds(60), Duration.ofSeconds(60),
+ PendingIntent.getActivity(UsageStatsActivity.this, 1, intent, 0));
}
}
});