diff options
39 files changed, 1453 insertions, 396 deletions
diff --git a/api/current.txt b/api/current.txt index 84dd79553e23..40b0d58208a4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -22858,7 +22858,6 @@ package android.location { 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(); @@ -22871,7 +22870,6 @@ package android.location { 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(); @@ -22887,7 +22885,6 @@ package android.location { 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); @@ -45145,8 +45142,8 @@ package android.telephony { 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(); @@ -45173,8 +45170,8 @@ package android.telephony { 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(); @@ -47113,7 +47110,7 @@ package android.text.style { public static class LineHeightSpan.Standard implements android.text.style.LineHeightSpan android.text.ParcelableSpan { ctor public LineHeightSpan.Standard(@Px @IntRange(from=1) int); - ctor public LineHeightSpan.Standard(android.os.Parcel); + ctor public LineHeightSpan.Standard(@NonNull android.os.Parcel); method public void chooseHeight(@NonNull CharSequence, int, int, int, int, @NonNull android.graphics.Paint.FontMetricsInt); method public int describeContents(); method @Px public int getHeight(); diff --git a/api/system-current.txt b/api/system-current.txt index 652e7eb01061..752640bb8d02 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1188,7 +1188,7 @@ package android.app.usage { 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); @@ -5689,6 +5689,7 @@ package android.permission { 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); @@ -7950,7 +7951,7 @@ package android.telephony { 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(); diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp index 012310cc277a..77a56e55045b 100644 --- a/cmds/incident_helper/src/ih_util.cpp +++ b/cmds/incident_helper/src/ih_util.cpp @@ -142,7 +142,7 @@ record_t parseRecordByColumns(const std::string& line, const std::vector<int>& i } if (lineSize - lastIndex > 0) { int beginning = lastIndex; - if (record.size() == indices.size()) { + if (record.size() == indices.size() && !record.empty()) { // We've already encountered all of the columns...put whatever is // left in the last column. record.pop_back(); diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp index 21ced9cb485c..5d525e6c7f3e 100644 --- a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp +++ b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp @@ -65,8 +65,9 @@ CpuInfoParser::Parse(const int in, const int out) const if (line.empty()) continue; nline++; - - if (stripPrefix(&line, "Tasks:")) { + // The format changes from time to time in toybox/toys/posix/ps.c + // With -H, it prints Threads instead of Tasks (FLAG(H)?"Thread":"Task") + if (stripPrefix(&line, "Threads:")) { writeSuffixLine(&proto, CpuInfoProto::TASK_STATS, line, COMMA_DELIMITER, CpuInfoProto::TaskStats::_FIELD_COUNT, CpuInfoProto::TaskStats::_FIELD_NAMES, diff --git a/cmds/incident_helper/testdata/cpuinfo.txt b/cmds/incident_helper/testdata/cpuinfo.txt index ec4a83960698..aa3afc33ad6a 100644 --- a/cmds/incident_helper/testdata/cpuinfo.txt +++ b/cmds/incident_helper/testdata/cpuinfo.txt @@ -1,8 +1,8 @@ -Tasks: 2038 total, 1 running,2033 sleeping, 0 stopped, 0 zombie +Threads: 2038 total, 1 running,2033 sleeping, 0 stopped, 0 zombie -Mem: 3842668k total, 3761936k used, 80732k free, 220188k buffers + Mem: 3842668k total, 3761936k used, 80732k free, 220188k buffers -Swap: 524284k total, 25892k used, 498392k free, 1316952k cached + Swap: 524284k total, 25892k used, 498392k free, 1316952k cached 400%cpu 17%user 0%nice 43%sys 338%idle 0%iow 0%irq 1%sirq 0%host @@ -12,4 +12,4 @@ Swap: 524284k total, 25892k used, 498392k free, 1316952k cached 29438 29438 rootabcdefghij 20 0 57.9 R 14M 3.8M top test top 916 916 system 18 -2 1.4 S 4.6G 404M fg system_server system_server 28 28 root -2 0 1.4 S 0 0 bg rcuc/3 [rcuc/3] - 27 27 root RT 0 1.4 S 0 0 ta migration/3 [migration/3]
\ No newline at end of file + 27 27 root RT 0 1.4 S 0 0 ta migration/3 [migration/3] diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl index b72ec39a5543..62b24e9995e8 100644 --- a/core/java/android/app/usage/IUsageStatsManager.aidl +++ b/core/java/android/app/usage/IUsageStatsManager.aidl @@ -60,7 +60,7 @@ interface IUsageStatsManager { 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 d34e6d3836b8..cee6b87fd3f1 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -35,6 +35,7 @@ import android.util.ArrayMap; 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 @@ public final class UsageStatsManager { } /** + * @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 @@ public final class UsageStatsManager { * @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 @@ public final class UsageStatsManager { 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/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl index cb2517e956df..76e911ddaf5a 100644 --- a/core/java/android/permission/IPermissionController.aidl +++ b/core/java/android/permission/IPermissionController.aidl @@ -40,6 +40,7 @@ oneway interface IPermissionController { 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 9d58064ab6d0..5695e42bef8e 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -476,6 +476,26 @@ public final class PermissionControllerManager { } /** + * 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 @@ public final class PermissionControllerManager { } } } + + /** + * 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 ee03689f211d..d375c10e6d4c 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -179,12 +179,21 @@ public abstract class PermissionControllerService extends Service { * @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 @@ public abstract class PermissionControllerService extends Service { } @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 @@ public abstract class PermissionControllerService extends Service { 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/Settings.java b/core/java/android/provider/Settings.java index 368fc2a12c92..45219d6f2e33 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3321,6 +3321,17 @@ public final class Settings { 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/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java index a5d5af2433fe..7fb0f950f583 100644 --- a/core/java/android/text/style/LineHeightSpan.java +++ b/core/java/android/text/style/LineHeightSpan.java @@ -88,7 +88,7 @@ public interface LineHeightSpan extends ParagraphStyle, WrapTogetherSpan { /** * Constructor called from {@link TextUtils} to restore the span from a parcel */ - public Standard(Parcel src) { + public Standard(@NonNull Parcel src) { mHeight = src.readInt(); } diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 47e546210103..2f2fdca7f279 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 426d813885fe..d5444c0e760c 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 69632c1d4167..ebc6be72bea6 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -92,7 +92,8 @@ public class SettingsBackupTest { 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 2110a8fa7e3d..3e53a3838543 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/location/java/android/location/Location.java b/location/java/android/location/Location.java index ed74333da895..17e25097cef8 100644 --- a/location/java/android/location/Location.java +++ b/location/java/android/location/Location.java @@ -113,10 +113,6 @@ public class Location implements Parcelable { * 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 @@ public class Location implements Parcelable { 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 @@ public class Location implements Parcelable { 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 @@ public class Location implements Parcelable { mProvider = null; mTime = 0; mElapsedRealtimeNanos = 0; - mElapsedRealtimeUncertaintyNanos = 0; mFieldsMask = 0; mLatitude = 0; mLongitude = 0; @@ -595,37 +586,6 @@ public class Location implements Parcelable { } /** - * 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 @@ public class Location implements Parcelable { 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 @@ public class Location implements Parcelable { 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 @@ public class Location implements Parcelable { parcel.writeString(mProvider); parcel.writeLong(mTime); parcel.writeLong(mElapsedRealtimeNanos); - parcel.writeLong(mElapsedRealtimeUncertaintyNanos); parcel.writeByte(mFieldsMask); parcel.writeDouble(mLatitude); parcel.writeDouble(mLongitude); diff --git a/packages/SystemUI/res/drawable/ic_volume_remote.xml b/packages/SettingsLib/res/drawable/ic_volume_remote.xml index fe396d9cc496..fe396d9cc496 100644 --- a/packages/SystemUI/res/drawable/ic_volume_remote.xml +++ b/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 index 2e0512244ddf..2e0512244ddf 100644 --- a/packages/SystemUI/res/drawable/ic_volume_remote_mute.xml +++ b/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 000000000000..7e0654d72b47 --- /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 index 8b00eee29c6e..4ed115405f57 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.IRemoteVolumeController; 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.os.RemoteException; 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 @@ public class MediaSessions { 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 @@ public class MediaSessions { 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 @@ public class MediaSessions { 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 @@ public class MediaSessions { if (appLabel.length() > 0) { return appLabel; } - } catch (NameNotFoundException e) { } + } catch (NameNotFoundException e) { + } return pkg; } @@ -240,29 +255,11 @@ public class MediaSessions { } } - 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 @@ public class MediaSessions { @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 @@ public class MediaSessions { 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 @@ public class MediaSessions { 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 000000000000..4e770aeb861e --- /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 Binary files differdeleted file mode 100644 index 035a4dffb026..000000000000 --- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png +++ /dev/null diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 563b0077c7f4..507c82246293 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -148,6 +148,7 @@ public class KeyguardClockSwitch extends RelativeLayout { 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 c6d62181540e..7edb5a55a9ae 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 @@ class Util { 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; - } - } - - 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); + 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; } - 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 32bc01cdfdfe..4c16297154f3 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.util.Log; 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/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index e9ae516cc8d0..9882f6c0d4c1 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 @@ abstract class DisplayDevice { } /** - * 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 setAllowedDisplayModesLocked(int[] modes) { + } + + /** + * Sets the requested color mode. */ - public void requestDisplayModesLocked(int colorMode, int modeId) { + 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 6c00da20b91e..32f34b8378ce 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 @@ public final class DisplayManagerService extends SystemService { 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 @@ public final class DisplayManagerService extends SystemService { 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 @@ public final class DisplayManagerService extends SystemService { 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 @@ public final class DisplayManagerService extends SystemService { // 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 @@ public final class DisplayManagerService extends SystemService { 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 @@ public final class DisplayManagerService extends SystemService { 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 @@ public final class DisplayManagerService extends SystemService { 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 @@ public final class DisplayManagerService extends SystemService { display.dumpLocked(ipw); } + pw.println(); + mDisplayModeDirector.dump(pw); + final int callbackCount = mCallbacks.size(); pw.println(); pw.println("Callbacks: size=" + callbackCount); @@ -2431,4 +2454,10 @@ public final class DisplayManagerService extends SystemService { } } + + 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 000000000000..af4db0730071 --- /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 77df10bf536a..5e5ef26f6624 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 @@ final class LocalDisplayAdapter extends DisplayAdapter { 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 @@ final class LocalDisplayAdapter extends DisplayAdapter { 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 @@ final class LocalDisplayAdapter extends DisplayAdapter { 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 @@ final class LocalDisplayAdapter extends DisplayAdapter { 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 @@ final class LocalDisplayAdapter extends DisplayAdapter { 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 @@ final class LocalDisplayAdapter extends DisplayAdapter { } mDefaultModeId = activeRecord.mMode.getModeId(); } + // Determine whether the active mode is still there. if (mSupportedModes.indexOfKey(mActiveModeId) < 0) { if (mActiveModeId != 0) { @@ -284,6 +294,21 @@ final class LocalDisplayAdapter extends DisplayAdapter { 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 @@ final class LocalDisplayAdapter extends DisplayAdapter { 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 @@ final class LocalDisplayAdapter extends DisplayAdapter { } @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 @@ final class LocalDisplayAdapter extends DisplayAdapter { 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 @@ final class LocalDisplayAdapter extends DisplayAdapter { 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 @@ final class LocalDisplayAdapter extends DisplayAdapter { } @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 b21f0a7f0144..a7b90510e6c8 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 @@ final class LogicalDisplay { // 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 @@ final class LogicalDisplay { // 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 @@ final class LogicalDisplay { } /** - * 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 @@ final class LogicalDisplay { 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 2f507d17730e..60cfbd0da2c3 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.Gravity; 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 @@ final class OverlayDisplayAdapter extends DisplayAdapter { } @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 9ab99753ed00..71ec5b00608e 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 @@ public class GnssLocationProvider extends AbstractLocationProvider implements // 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 @@ public class GnssLocationProvider extends AbstractLocationProvider implements 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 @@ public class GnssLocationProvider extends AbstractLocationProvider implements 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 e8882ec12244..d39f20c0f214 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 @@ static jobject translateGnssLocation(JNIEnv* env, SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs); } - if (flags & ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS) { - SET(ElapsedUncertaintyRealtimeNanos, location.elapsedRealtime.timeUncertaintyNs); - } - return object.get(); } @@ -525,8 +521,7 @@ static GnssLocation_V2_0 createGnssLocation_V2_0( 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 @@ static GnssLocation_V2_0 createGnssLocation_V2_0( 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 @@ static void android_location_GnssLocationProvider_inject_best_location( 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 @@ static void android_location_GnssLocationProvider_inject_best_location( bearingAccuracyDegrees, timestamp, elapsedRealtimeFlags, - elapsedRealtimeNanos, - elapsedRealtimeUncertaintyNanos); + elapsedRealtimeNanos); auto result = gnssHal_V2_0->injectBestLocation_2_0(location); if (!result.isOk() || !result) { @@ -2821,7 +2813,7 @@ static const JNINativeMethod sMethods[] = { 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 8caa39dfc9e7..1f861716d380 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 @@ public class AppTimeLimitControllerTests { /** 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 @@ public class AppTimeLimitControllerTests { /** 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 @@ public class AppTimeLimitControllerTests { /** 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 class AppTimeLimitControllerTests { 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 @@ public class AppTimeLimitControllerTests { @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 @@ public class AppTimeLimitControllerTests { @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 @@ public class AppTimeLimitControllerTests { @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 @@ public class AppTimeLimitControllerTests { 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 @@ public class AppTimeLimitControllerTests { 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 class AppTimeLimitControllerTests { 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 class AppTimeLimitControllerTests { 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 @@ public class AppTimeLimitControllerTests { @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 @@ public class AppTimeLimitControllerTests { /** 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 @@ public class AppTimeLimitControllerTests { @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 @@ public class AppTimeLimitControllerTests { */ @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 @@ public class AppTimeLimitControllerTests { @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 @@ public class AppTimeLimitControllerTests { @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 @@ public class AppTimeLimitControllerTests { 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 @@ public class AppTimeLimitControllerTests { 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 731cbf42eca7..f3d63873dc4b 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 @@ public class AppTimeLimitController { 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 @@ public class AppTimeLimitController { * 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 @@ public class AppTimeLimitController { "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 b14d7228dcb4..27fdbcb80275 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 @@ public class UsageStatsService extends SystemService implements @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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements } 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/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index feff01c09b1e..aaf5f33346f5 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1566,6 +1566,7 @@ public class TelephonyManager { * 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 @@ public class TelephonyManager { * * @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 @@ public class TelephonyManager { * 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 @@ public class TelephonyManager { * * @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 @@ public class TelephonyManager { * @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() { diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java index 0105893adf9e..adcd11a08bff 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.EditText; 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 @@ public class UsageStatsActivity extends ListActivity { 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)); } } }); |