diff options
158 files changed, 3224 insertions, 984 deletions
diff --git a/Android.bp b/Android.bp index d4ca7066781a..03a6af5997bb 100644 --- a/Android.bp +++ b/Android.bp @@ -339,9 +339,7 @@ java_defaults { "sax/java", "telecomm/java", - // TODO(b/148660295): remove this - "apex/media/framework/java", - + "apex/media/aidl/stable", // TODO(b/147699819): remove this "telephony/java", ], diff --git a/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java index 73b4a1914ad1..836e6b617395 100644 --- a/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java +++ b/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java @@ -192,6 +192,11 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase { Assume.assumeNoException( new AssertionError("onAnimationCanceled should not be called")); } + + @Override + public void onTaskAppeared(RemoteAnimationTarget app) throws RemoteException { + /* no-op */ + } }; recentsSemaphore.tryAcquire(); diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index b6f85b2d06bf..1ab79380fb0d 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -1364,6 +1364,19 @@ public class AppStandbyController implements AppStandbyInternal { if (DEBUG) { Slog.d(TAG, " Keeping at WORKING_SET due to min timeout"); } + } else if (newBucket == STANDBY_BUCKET_RARE + && getBucketForLocked(packageName, userId, elapsedRealtime) + == STANDBY_BUCKET_RESTRICTED) { + // Prediction doesn't think the app will be used anytime soon and + // it's been long enough that it could just time out into restricted, + // so time it out there instead. Using TIMEOUT will allow prediction + // to raise the bucket when it needs to. + newBucket = STANDBY_BUCKET_RESTRICTED; + reason = REASON_MAIN_TIMEOUT; + if (DEBUG) { + Slog.d(TAG, + "Prediction to RARE overridden by timeout into RESTRICTED"); + } } } diff --git a/apex/media/aidl/Android.bp b/apex/media/aidl/Android.bp new file mode 100644 index 000000000000..409a04897f56 --- /dev/null +++ b/apex/media/aidl/Android.bp @@ -0,0 +1,35 @@ +// +// Copyright 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +filegroup { + name: "stable-mediasession2-aidl-srcs", + srcs: ["stable/**/*.aidl"], + path: "stable", +} + +filegroup { + name: "private-mediasession2-aidl-srcs", + srcs: ["private/**/I*.aidl"], + path: "private", +} + +filegroup { + name: "mediasession2-aidl-srcs", + srcs: [ + ":private-mediasession2-aidl-srcs", + ":stable-mediasession2-aidl-srcs", + ], +} diff --git a/apex/media/framework/java/android/media/Controller2Link.aidl b/apex/media/aidl/private/android/media/Controller2Link.aidl index 64edafcb11fc..64edafcb11fc 100644 --- a/apex/media/framework/java/android/media/Controller2Link.aidl +++ b/apex/media/aidl/private/android/media/Controller2Link.aidl diff --git a/apex/media/framework/java/android/media/IMediaController2.aidl b/apex/media/aidl/private/android/media/IMediaController2.aidl index 42c6e70529ec..42c6e70529ec 100644 --- a/apex/media/framework/java/android/media/IMediaController2.aidl +++ b/apex/media/aidl/private/android/media/IMediaController2.aidl diff --git a/apex/media/framework/java/android/media/IMediaSession2.aidl b/apex/media/aidl/private/android/media/IMediaSession2.aidl index 26e717b39afc..26e717b39afc 100644 --- a/apex/media/framework/java/android/media/IMediaSession2.aidl +++ b/apex/media/aidl/private/android/media/IMediaSession2.aidl diff --git a/apex/media/framework/java/android/media/IMediaSession2Service.aidl b/apex/media/aidl/private/android/media/IMediaSession2Service.aidl index 10ac1be0a36e..10ac1be0a36e 100644 --- a/apex/media/framework/java/android/media/IMediaSession2Service.aidl +++ b/apex/media/aidl/private/android/media/IMediaSession2Service.aidl diff --git a/apex/media/framework/java/android/media/Session2Command.aidl b/apex/media/aidl/private/android/media/Session2Command.aidl index 43a7b123ed29..43a7b123ed29 100644 --- a/apex/media/framework/java/android/media/Session2Command.aidl +++ b/apex/media/aidl/private/android/media/Session2Command.aidl diff --git a/apex/media/framework/java/android/media/Session2Token.aidl b/apex/media/aidl/stable/android/media/Session2Token.aidl index c5980e9e77fd..c5980e9e77fd 100644 --- a/apex/media/framework/java/android/media/Session2Token.aidl +++ b/apex/media/aidl/stable/android/media/Session2Token.aidl diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp index 579963b71cf7..34fe22879ca9 100644 --- a/apex/media/framework/Android.bp +++ b/apex/media/framework/Android.bp @@ -55,17 +55,15 @@ filegroup { name: "updatable-media-srcs", srcs: [ ":mediaparser-srcs", - ":mediasession2-srcs", + ":mediasession2-java-srcs", + ":mediasession2-aidl-srcs", ], } filegroup { - name: "mediasession2-srcs", + name: "mediasession2-java-srcs", srcs: [ "java/android/media/Controller2Link.java", - "java/android/media/IMediaController2.aidl", - "java/android/media/IMediaSession2.aidl", - "java/android/media/IMediaSession2Service.aidl", "java/android/media/MediaConstants.java", "java/android/media/MediaController2.java", "java/android/media/MediaSession2.java", @@ -83,7 +81,7 @@ filegroup { srcs: [ "java/android/media/MediaParser.java" ], - path: "java" + path: "java", } stubs_defaults { diff --git a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl index b94928f09ae0..5cdb3249501b 100644 --- a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl +++ b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl @@ -59,9 +59,6 @@ interface IStatsCompanionService { /** Cancel any alarm for the purpose of subscriber triggering. */ oneway void cancelAlarmForSubscriberTriggering(); - /** Tells StatsCompaionService to grab the uid map snapshot and send it to statsd. */ - oneway void triggerUidSnapshot(); - /** * Ask StatsCompanionService if the given permission is allowed for a particular process * and user ID. statsd is incapable of doing this check itself because checkCallingPermission diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java index aad51124c8d2..66e41cca96a7 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java @@ -153,7 +153,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - private static void informAllUidsLocked(Context context) throws RemoteException { + private static void informAllUids(Context context) { UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); PackageManager pm = context.getPackageManager(); final List<UserHandle> users = um.getUserHandles(true); @@ -168,18 +168,26 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { Log.e(TAG, "Failed to create a pipe to send uid map data.", e); return; } - sStatsd.informAllUidData(fds[0]); - try { - fds[0].close(); - } catch (IOException e) { - Log.e(TAG, "Failed to close the read side of the pipe.", e); - } - final ParcelFileDescriptor writeFd = fds[1]; HandlerThread backgroundThread = new HandlerThread( "statsCompanionService.bg", THREAD_PRIORITY_BACKGROUND); backgroundThread.start(); Handler handler = new Handler(backgroundThread.getLooper()); handler.post(() -> { + IStatsd statsd = getStatsdNonblocking(); + if (statsd == null) { + return; + } + try { + statsd.informAllUidData(fds[0]); + } catch (RemoteException e) { + Log.e(TAG, "Failed to send uid map to statsd"); + } + try { + fds[0].close(); + } catch (IOException e) { + Log.e(TAG, "Failed to close the read side of the pipe.", e); + } + final ParcelFileDescriptor writeFd = fds[1]; FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(writeFd); try { ProtoOutputStream output = new ProtoOutputStream(fout); @@ -188,7 +196,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { for (UserHandle userHandle : users) { List<PackageInfo> pi = pm.getInstalledPackagesAsUser(PackageManager.MATCH_UNINSTALLED_PACKAGES - | PackageManager.MATCH_ANY_USER, + | PackageManager.MATCH_ANY_USER + | PackageManager.MATCH_APEX, userHandle.getIdentifier()); for (int j = 0; j < pi.size(); j++) { if (pi.get(j).applicationInfo != null) { @@ -319,19 +328,9 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private static final class UserUpdateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - synchronized (sStatsdLock) { - if (sStatsd == null) { - Log.w(TAG, "Could not access statsd for UserUpdateReceiver"); - return; - } - try { - // Pull the latest state of UID->app name, version mapping. - // Needed since the new user basically has a version of every app. - informAllUidsLocked(context); - } catch (RemoteException e) { - Log.e(TAG, "Failed to inform statsd latest update of all apps", e); - } - } + // Pull the latest state of UID->app name, version mapping. + // Needed since the new user basically has a version of every app. + informAllUids(context); } } @@ -589,21 +588,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } @Override // Binder call - public void triggerUidSnapshot() { - StatsCompanion.enforceStatsdCallingUid(); - synchronized (sStatsdLock) { - final long token = Binder.clearCallingIdentity(); - try { - informAllUidsLocked(mContext); - } catch (RemoteException e) { - Log.e(TAG, "Failed to trigger uid snapshot.", e); - } finally { - Binder.restoreCallingIdentity(token); - } - } - } - - @Override // Binder call public boolean checkPermission(String permission, int pid, int uid) { StatsCompanion.enforceStatsdCallingUid(); return mContext.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED; @@ -707,7 +691,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { try { // Pull the latest state of UID->app name, version mapping when // statsd starts. - informAllUidsLocked(mContext); + informAllUids(mContext); } finally { Binder.restoreCallingIdentity(token); } diff --git a/api/current.txt b/api/current.txt index b4db1f7bc10a..80e2d00b3e48 100644 --- a/api/current.txt +++ b/api/current.txt @@ -46230,7 +46230,7 @@ package android.telecom { method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts(); method public String getDefaultDialerPackage(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(String); - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1Number(android.telecom.PhoneAccountHandle); + method @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}, conditional=true) public String getLine1Number(android.telecom.PhoneAccountHandle); method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getSelfManagedPhoneAccounts(); method public android.telecom.PhoneAccountHandle getSimCallManager(); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 2a1716e0ce2a..453ddeb332af 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -384,12 +384,12 @@ message Atom { PerfettoUploaded perfetto_uploaded = 229 [(module) = "perfetto"]; VmsClientConnectionStateChanged vms_client_connection_state_changed = 230 [(module) = "car"]; - MediaProviderScanEvent media_provider_scan_event = 233 [(module) = "mediaprovider"]; - MediaProviderDeletionEvent media_provider_deletion_event = 234 [(module) = "mediaprovider"]; - MediaProviderPermissionEvent media_provider_permission_event = + MediaProviderScanOccurred media_provider_scan_occurred = 233 [(module) = "mediaprovider"]; + MediaContentDeleted media_content_deleted = 234 [(module) = "mediaprovider"]; + MediaProviderPermissionRequested media_provider_permission_requested = 235 [(module) = "mediaprovider"]; - MediaProviderSchemaChange media_provider_schema_change = 236 [(module) = "mediaprovider"]; - MediaProviderIdleMaintenance media_provider_idle_maintenance = + MediaProviderSchemaChanged media_provider_schema_changed = 236 [(module) = "mediaprovider"]; + MediaProviderIdleMaintenanceFinished media_provider_idle_maintenance_finished = 237 [(module) = "mediaprovider"]; RebootEscrowRecoveryReported reboot_escrow_recovery_reported = 238 [(module) = "framework"]; BootTimeEventDuration boot_time_event_duration_reported = 239 [(module) = "framework"]; @@ -420,6 +420,7 @@ message Atom { RankingSelected ranking_selected = 260 [(module) = "framework"]; TvSettingsUIInteracted tvsettings_ui_interacted = 261 [(module) = "tv_settings"]; LauncherStaticLayout launcher_snapshot = 262 [(module) = "sysui"]; + PackageInstallerV2Reported package_installer_v2_reported = 263 [(module) = "framework"]; SdkExtensionStatus sdk_extension_status = 354; } @@ -4455,7 +4456,7 @@ message VmsClientConnectionStateChanged { * Logged from: * packages/providers/MediaProvider/src/com/android/providers/media/scan/ModernMediaScanner.java */ -message MediaProviderScanEvent { +message MediaProviderScanOccurred { enum Reason { // Scan triggered due to unknown reason UNKNOWN = 0; @@ -4489,15 +4490,13 @@ message MediaProviderScanEvent { * Logged from: * packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java */ -message MediaProviderDeletionEvent { +message MediaContentDeleted { // Volume type that this event pertains to optional android.stats.mediaprovider.VolumeType volume_type = 1; - // Device timestamp when this deletion event occurred - optional int64 timestamp_millis = 2; - // App that requested deletion - optional string package_name = 3; + // UID of app that requested deletion + optional int32 uid = 2 [(is_uid) = true]; // Number of items that were deleted - optional int32 item_count = 4; + optional int32 item_count = 3; } /** @@ -4506,7 +4505,7 @@ message MediaProviderDeletionEvent { * Logged from: * packages/providers/MediaProvider/src/com/android/providers/media/PermissionActivity.java */ -message MediaProviderPermissionEvent { +message MediaProviderPermissionRequested { enum Result { UNKNOWN = 0; USER_GRANTED = 1; @@ -4518,14 +4517,12 @@ message MediaProviderPermissionEvent { // Volume type that this event pertains to optional android.stats.mediaprovider.VolumeType volume_type = 1; - // Device timestamp when this permission event occurred - optional int64 timestamp_millis = 2; - // App that requested permission - optional string package_name = 3; + // UID of app that requested permission + optional int32 uid = 2 [(is_uid) = true]; // Number of items that were requested - optional int32 item_count = 4; + optional int32 item_count = 3; // Result of this request - optional Result result = 5; + optional Result result = 4; } /** @@ -4534,7 +4531,7 @@ message MediaProviderPermissionEvent { * Logged from: * packages/providers/MediaProvider/src/com/android/providers/media/DatabaseHelper.java */ -message MediaProviderSchemaChange { +message MediaProviderSchemaChanged { // Volume type that this event pertains to optional android.stats.mediaprovider.VolumeType volume_type = 1; // Old database version code @@ -4553,7 +4550,7 @@ message MediaProviderSchemaChange { * Logged from: * packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java */ -message MediaProviderIdleMaintenance { +message MediaProviderIdleMaintenanceFinished { // Volume type that this event pertains to optional android.stats.mediaprovider.VolumeType volume_type = 1; @@ -9308,6 +9305,25 @@ message TvSettingsUIInteracted { } /** + * Logs information about a package installation using package installer V2 APIs. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java + */ +message PackageInstallerV2Reported { + // Whether this installation uses Incremental File System + optional bool is_incremental = 1; + // Name of the package that is intended to be installed + optional string package_name = 2; + // The duration between when the install was requested to when the install has completed + optional int64 duration_millis = 3; + // Installation result in final integer, which are SystemApi's. + // Return_code 1 indicates success. + // For full list, see frameworks/base/core/java/android/content/pm/PackageManager.java + optional int32 return_code = 4; +} + +/** * Logs settings provider values. * * Use DeviceConfig.getProperties to get a list Setting key, query the data from content provider, diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java index d5da0b42402c..54a744b654cb 100644 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java +++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java @@ -64,7 +64,8 @@ public class TestDrive { "AID_LMKD", "com.android.managedprovisioning", "AID_MEDIA", - "AID_NETWORK_STACK" + "AID_NETWORK_STACK", + "com.google.android.providers.media.module", }; private static final String[] DEFAULT_PULL_SOURCES = { "AID_SYSTEM", diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java index 3180b77f5700..957ebfbef799 100644 --- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java +++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java @@ -24,6 +24,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; +import android.sysprop.InitProperties; public class PowerCommand extends Svc.Command { private static final int FORCE_SUSPEND_DELAY_DEFAULT_MILLIS = 0; @@ -103,6 +104,8 @@ public class PowerCommand extends Svc.Command { pm.reboot(false, mode, true); } catch (RemoteException e) { maybeLogRemoteException("Failed to reboot."); + } catch (Exception e) { + System.err.println("Failed to reboot: " + e.getMessage()); } return; } else if ("shutdown".equals(args[1])) { @@ -138,7 +141,9 @@ public class PowerCommand extends Svc.Command { // if it is already in shutdown flow. private void maybeLogRemoteException(String msg) { String powerProp = SystemProperties.get("sys.powerctl"); - if (powerProp.isEmpty()) { + // Also check if userspace reboot is ongoing, since in case of userspace reboot value of the + // sys.powerctl property will be reset. + if (powerProp.isEmpty() && !InitProperties.userspace_reboot_in_progress().orElse(false)) { System.err.println(msg); } } diff --git a/core/java/android/app/AppOps.md b/core/java/android/app/AppOps.md index bee701addca8..ad1a30146663 100644 --- a/core/java/android/app/AppOps.md +++ b/core/java/android/app/AppOps.md @@ -116,14 +116,27 @@ for app-ops. It also delays the changes by a _settle time_. This delay is needed can fluctuate when switching apps. By delaying the change the appops service is not affected by those. -The proc state is used for two use cases: Firstly, Tracking remembers the proc state for each -tracked event. Secondly, `noteOp`/`checkOp` calls for app-op that are set to `MODE_FOREGROUND` are -translated using the `AppOpsService.UidState.evalMode` method into `MODE_ALLOWED` when the app is -counted as foreground and `MODE_IGNORED` when the app is counted as background. `checkOpRaw` -calls are not affected. - -The current proc state for an app can be read from `dumpsys appops`. The tracking information can -be read from `dumpsys appops` +In addition to proc state, the `AppOpsService` also receives process capability update from the +`ActivityManagerService`. Proc capability specifies what while-in-use(`MODE_FOREGROUND`) operations + the proc is allowed to perform in its current proc state. There are three proc capabilities + defined so far: +`PROCESS_CAPABILITY_FOREGROUND_LOCATION`, `PROCESS_CAPABILITY_FOREGROUND_CAMERA` and +`PROCESS_CAPABILITY_FOREGROUND_MICROPHONE`, they correspond to the while-in-use operation of +location, camera and microphone (microphone is `RECORD_AUDIO`). + +In `ActivityManagerService`, `PROCESS_STATE_TOP` and `PROCESS_STATE_PERSISTENT` have all +three capabilities, `PROCESS_STATE_FOREGROUND_SERVICE` has capabilities defined by + `foregroundServiceType` that is specified in foreground service's manifest file. A client process + can pass its capabilities to service using `BIND_INCLUDE_CAPABILITIES` flag. + +The proc state and capability are used for two use cases: Firstly, Tracking remembers the proc state + for each tracked event. Secondly, `noteOp`/`checkOp` calls for app-op that are set to + `MODE_FOREGROUND` are translated using the `AppOpsService.UidState.evalMode` method into + `MODE_ALLOWED` when the app has the capability and `MODE_IGNORED` when the app does not have the + capability. `checkOpRaw` calls are not affected. + +The current proc state and capability for an app can be read from `dumpsys appops`. +The tracking information can be read from `dumpsys appops` ``` Uid u0a118: diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 7a593498e967..babeffc120bb 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -7589,10 +7589,8 @@ public class Notification implements Parcelable >= Build.VERSION_CODES.P; boolean isOneToOne; CharSequence nameReplacement = null; - Icon avatarReplacement = null; if (!atLeastP) { isOneToOne = TextUtils.isEmpty(conversationTitle); - avatarReplacement = mBuilder.mN.mLargeIcon; if (hasOnlyWhiteSpaceSenders()) { isOneToOne = true; nameReplacement = conversationTitle; @@ -7641,7 +7639,7 @@ public class Notification implements Parcelable contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsCollapsed", isCollapsed); contentView.setIcon(R.id.status_bar_latest_event_content, "setAvatarReplacement", - avatarReplacement); + mBuilder.mN.mLargeIcon); contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement", nameReplacement); contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsOneToOne", diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index faf9ec61ffde..fb9adb730314 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -8606,7 +8606,7 @@ public class DevicePolicyManager { * <p> * This method may be called on the {@code DevicePolicyManager} instance returned from * {@link #getParentProfileInstance(ComponentName)}. Note that only a profile owner on - * an organization-deviced can affect account types on the parent profile instance. + * an organization-owned device can affect account types on the parent profile instance. * * @return a list of account types for which account management has been disabled. * diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index b8e1aa88c3a3..be2de0edda2d 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1483,12 +1483,25 @@ public final class PowerManager { return mInteractiveCache.query(null); } + + /** + * Returns {@code true} if this device supports rebooting userspace. + * + * <p>This method exists solely for the sake of re-using same logic between {@code PowerManager} + * and {@code PowerManagerService}. + * + * @hide + */ + public static boolean isRebootingUserspaceSupportedImpl() { + return InitProperties.is_userspace_reboot_supported().orElse(false); + } + /** * Returns {@code true} if this device supports rebooting userspace. */ // TODO(b/138605180): add link to documentation once it's ready. public boolean isRebootingUserspaceSupported() { - return InitProperties.is_userspace_reboot_supported().orElse(false); + return isRebootingUserspaceSupportedImpl(); } /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 920302c75011..541883346e23 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1870,6 +1870,24 @@ public final class Settings { public static final String EXTRA_APP_UID = "app_uid"; /** + * Activity Action: Show power menu settings. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_POWER_MENU_SETTINGS = + "android.settings.ACTION_POWER_MENU_SETTINGS"; + + /** + * Activity Action: Show controls settings. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_DEVICE_CONTROLS_SETTINGS = + "android.settings.ACTION_DEVICE_CONTROLS_SETTINGS"; + + /** * Activity Action: Show a dialog with disabled by policy message. * <p> If an user action is disabled by policy, this dialog can be triggered to let * the user know about this. diff --git a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl index dd434b440af4..bf0bb9e2a41f 100644 --- a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl +++ b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl @@ -17,6 +17,7 @@ package android.service.autofill; import android.os.IBinder; +import android.os.RemoteCallback; import android.service.autofill.IInlineSuggestionUiCallback; import android.service.autofill.InlinePresentation; @@ -29,4 +30,5 @@ oneway interface IInlineSuggestionRenderService { void renderSuggestion(in IInlineSuggestionUiCallback callback, in InlinePresentation presentation, int width, int height, in IBinder hostInputToken, int displayId); + void getInlineSuggestionsRendererInfo(in RemoteCallback callback); } diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java index cba6608db1b8..e3ed21ff556d 100644 --- a/core/java/android/service/autofill/InlineSuggestionRenderService.java +++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java @@ -30,6 +30,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.RemoteCallback; import android.os.RemoteException; import android.util.Log; import android.view.Display; @@ -128,6 +129,11 @@ public abstract class InlineSuggestionRenderService extends Service { } } + private void handleGetInlineSuggestionsRendererInfo(@NonNull RemoteCallback callback) { + final Bundle rendererInfo = onGetInlineSuggestionsRendererInfo(); + callback.sendResult(rendererInfo); + } + private void sendResult(@NonNull IInlineSuggestionUiCallback callback, @Nullable SurfaceControlViewHost.SurfacePackage surface) { try { @@ -151,6 +157,13 @@ public abstract class InlineSuggestionRenderService extends Service { InlineSuggestionRenderService.this, callback, presentation, width, height, hostInputToken, displayId)); } + + @Override + public void getInlineSuggestionsRendererInfo(@NonNull RemoteCallback callback) { + mHandler.sendMessage(obtainMessage( + InlineSuggestionRenderService::handleGetInlineSuggestionsRendererInfo, + InlineSuggestionRenderService.this, callback)); + } }.asBinder(); } diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index ef55f0615ec7..46cb65babc1c 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -747,7 +747,7 @@ public abstract class ContentCaptureService extends Service { void initializeForDelegate(DataShareReadAdapterDelegate delegate, DataShareReadAdapter adapter, Executor executor) { mDataShareReadAdapterHardReferences.put(delegate, adapter); - mExecutorHardReferences.remove(delegate, executor); + mExecutorHardReferences.put(delegate, executor); } Executor getExecutor(DataShareReadAdapterDelegate delegate) { diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index a60a5cca08bd..983ab2eedf5c 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -114,4 +114,16 @@ interface IRecentsAnimationController { * animation is cancelled through fail safe mechanism. */ void setWillFinishToHome(boolean willFinishToHome); + + /** + * Stops controlling a task that is currently controlled by this recents animation. + * + * This method should be called when a task that has been received via {@link #onAnimationStart} + * or {@link #onTaskAppeared} is no longer needed. After calling this method, the task will + * either disappear from the screen, or jump to its final position in case it was the top task. + * + * @param taskId Id of the Task target to remove + * @return {@code true} when target removed successfully, {@code false} otherwise. + */ + boolean removeTask(int taskId); } diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl index 6eb90fc54286..925786f82e00 100644 --- a/core/java/android/view/IRecentsAnimationRunner.aidl +++ b/core/java/android/view/IRecentsAnimationRunner.aidl @@ -56,4 +56,10 @@ oneway interface IRecentsAnimationRunner { void onAnimationStart(in IRecentsAnimationController controller, in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers, in Rect homeContentInsets, in Rect minimizedHomeBounds) = 2; + + /** + * Called when the task of an activity that has been started while the recents animation + * was running becomes ready for control. + */ + void onTaskAppeared(in RemoteAnimationTarget app) = 3; } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 5648adfb78bc..b0bacb955f80 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -742,4 +742,11 @@ interface IWindowManager * Called to show global actions. */ void showGlobalActions(); + + /** + * Sets layer tracing flags for SurfaceFlingerTrace. + * + * @param flags see definition in SurfaceTracing.cpp + */ + void setLayerTracingFlags(int flags); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index da186087a34a..8abe72fc91e8 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -14684,17 +14684,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } } - if (isAccessibilityPane()) { - if (isVisible != oldVisible) { + + if (isVisible != oldVisible) { + if (isAccessibilityPane()) { notifyViewAccessibilityStateChangedIfNeeded(isVisible ? AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED : AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED); } - } - notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible); - if (!getSystemGestureExclusionRects().isEmpty() && isVisible != oldVisible) { - postUpdateSystemGestureExclusionRects(); + notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible); + + if (!getSystemGestureExclusionRects().isEmpty()) { + postUpdateSystemGestureExclusionRects(); + } } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 750b1eddd222..dab11088b130 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3017,6 +3017,10 @@ public final class ViewRootImpl implements ViewParent, if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { reportNextDraw(); } + if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC) != 0) { + reportNextDraw(); + setUseBLASTSyncTransaction(); + } boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible; @@ -3725,7 +3729,7 @@ public final class ViewRootImpl implements ViewParent, if (needFrameCompleteCallback) { final Handler handler = mAttachInfo.mHandler; mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> { - finishBLASTSync(); + finishBLASTSync(!reportNextDraw); handler.postAtFrontOfQueue(() -> { if (reportNextDraw) { // TODO: Use the frame number @@ -3759,7 +3763,7 @@ public final class ViewRootImpl implements ViewParent, if (usingAsyncReport && !canUseAsync) { mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null); usingAsyncReport = false; - finishBLASTSync(); + finishBLASTSync(true /* apply */); } } finally { mIsDrawing = false; @@ -9576,10 +9580,15 @@ public final class ViewRootImpl implements ViewParent, mNextDrawUseBLASTSyncTransaction = true; } - private void finishBLASTSync() { + private void finishBLASTSync(boolean apply) { if (mNextReportConsumeBLAST) { mNextReportConsumeBLAST = false; - mRtBLASTSyncTransaction.apply(); + + if (apply) { + mRtBLASTSyncTransaction.apply(); + } else { + mSurfaceChangedTransaction.merge(mRtBLASTSyncTransaction); + } } } diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 410d9afe73da..ab968d798745 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -102,6 +102,14 @@ public final class WindowManagerGlobal { public static final int RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS = 0x40; /** + * This flag indicates the client should not directly submit it's next frame, + * but instead should pass it in the postDrawTransaction of + * {@link WindowManagerService#finishDrawing}. This is used by the WM + * BLASTSyncEngine to synchronize rendering of multiple windows. + */ + public static final int RELAYOUT_RES_BLAST_SYNC = 0x80; + + /** * Flag for relayout: the client will be later giving * internal insets; as a result, the window will not impact other window * layouts until the insets are given. diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index edc6b1251f2c..4519aefe65fe 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -832,7 +832,7 @@ public final class ContentCaptureManager { void initializeForDelegate(DataShareAdapterDelegate delegate, DataShareWriteAdapter adapter, Executor executor) { mWriteAdapterHardReferences.put(delegate, adapter); - mExecutorHardReferences.remove(delegate, executor); + mExecutorHardReferences.put(delegate, executor); } Executor getExecutor(DataShareAdapterDelegate delegate) { diff --git a/core/java/android/window/TaskOrganizerTaskEmbedder.java b/core/java/android/window/TaskOrganizerTaskEmbedder.java index 2091c9398e95..39a0101bbf59 100644 --- a/core/java/android/window/TaskOrganizerTaskEmbedder.java +++ b/core/java/android/window/TaskOrganizerTaskEmbedder.java @@ -254,7 +254,9 @@ public class TaskOrganizerTaskEmbedder extends TaskEmbedder { mTaskToken = taskInfo.token; mTaskLeash = mTaskToken.getLeash(); mTransaction.reparent(mTaskLeash, mSurfaceControl) - .show(mSurfaceControl).apply(); + .show(mTaskLeash) + .show(mSurfaceControl) + .apply(); if (mPendingNotifyBoundsChanged) { // TODO: Either defer show or hide and synchronize show with the resize notifyBoundsChanged(); diff --git a/core/java/com/android/internal/util/FunctionalUtils.java b/core/java/com/android/internal/util/FunctionalUtils.java index 3c9791791a68..9168438dc2bf 100644 --- a/core/java/com/android/internal/util/FunctionalUtils.java +++ b/core/java/com/android/internal/util/FunctionalUtils.java @@ -16,6 +16,7 @@ package com.android.internal.util; +import android.annotation.NonNull; import android.os.RemoteException; import android.util.ExceptionUtils; @@ -218,4 +219,37 @@ public class FunctionalUtils { } } } + + // TODO: add unit test + /** + * Gets a user-friendly name for a lambda function. + */ + @NonNull + public static String getLambdaName(@NonNull Object function) { + // Full function has one of the following formats: + // package-$$Lambda$class$randomId + // package-$$Lambda$randomId + // + // We just want just package.class$Lambda (or package$Lambda) respectively + + final String fullFunction = function.toString(); + + final int endPkgIdx = fullFunction.indexOf("-$$"); + if (endPkgIdx == -1) return fullFunction; + + // firstDollarIdx could be either beginning of class or beginning of the random id + final int firstDollarIdx = fullFunction.indexOf('$', endPkgIdx + 3); + if (firstDollarIdx == -1) return fullFunction; + + final int endClassIdx = fullFunction.indexOf('$', firstDollarIdx + 1); + if (endClassIdx == -1) { + // Just package + return fullFunction.substring(0, endPkgIdx - 1) + "$Lambda"; + } + + // Package + class + return fullFunction.substring(0, endPkgIdx) + + fullFunction.substring(firstDollarIdx + 1, endClassIdx) + + "$Lambda"; + } } diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java index e4a44084e91c..1646a07b8001 100755 --- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java +++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java @@ -16,7 +16,6 @@ package com.android.internal.util.function.pooled; -import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Message; import android.text.TextUtils; @@ -25,6 +24,7 @@ import android.util.Pools; import com.android.internal.util.ArrayUtils; import com.android.internal.util.BitUtils; +import com.android.internal.util.FunctionalUtils; import com.android.internal.util.function.DecConsumer; import com.android.internal.util.function.DecFunction; import com.android.internal.util.function.DecPredicate; @@ -580,36 +580,6 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, return r; } - // TODO: add unit test - @NonNull - private static String getFriendlyName(@NonNull Object function) { - // Full function has one of the following formats: - // package-$$Lambda$class$randomId - // package-$$Lambda$randomId - // - // We just want just package.class$Lambda (or package$Lambda) respectively - - final String fullFunction = function.toString(); - - final int endPkgIdx = fullFunction.indexOf("-$$"); - if (endPkgIdx == -1) return fullFunction; - - // firstDollarIdx could be either beginning of class or beginning of the random id - final int firstDollarIdx = fullFunction.indexOf('$', endPkgIdx + 3); - if (firstDollarIdx == -1) return fullFunction; - - final int endClassIdx = fullFunction.indexOf('$', firstDollarIdx + 1); - if (endClassIdx == -1) { - // Just package - return fullFunction.substring(0, endPkgIdx - 1) + "$Lambda"; - } - - // Package + class - return fullFunction.substring(0, endPkgIdx) - + fullFunction.substring(firstDollarIdx + 1, endClassIdx) - + "$Lambda"; - } - private static void setIfInBounds(Object[] array, int i, Object a) { if (i < ArrayUtils.size(array)) array[i] = a; } @@ -651,7 +621,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, @Override public String getTraceName() { - return getFriendlyName(mFunc); + return FunctionalUtils.getLambdaName(mFunc); } private boolean isRecycled() { diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 4b30359e671a..924dc4b3a051 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -1653,7 +1653,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, uid, process_name, managed_nice_name, fail_fn); isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn); } - if ((mount_external != MOUNT_EXTERNAL_INSTALLER) && mount_storage_dirs) { + if (mount_external != MOUNT_EXTERNAL_INSTALLER && + mount_external != MOUNT_EXTERNAL_PASS_THROUGH && + mount_storage_dirs) { BindMountStorageDirs(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn); } diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 6321651c1345..3e007e4704a4 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2674,4 +2674,14 @@ enum PageId { // CATEGORY: SETTINGS // OS: R FUELGAUGE_ADVANCED_BATTERY_OPTION = 1842; + + // OPEN: Settings > System > Gestures > Power menu + // CATEGORY: SETTINGS + // OS: R + POWER_MENU_SETTINGS = 1843; + + // OPEN: Settings > System > Gestures > Power menu > Device controls + // CATEGORY: SETTINGS + // OS: R + DEVICE_CONTROLS_SETTINGS = 1844; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index c72239cd684e..1c978bff0fd3 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -351,8 +351,9 @@ <protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_ALLOWED_APP" /> <protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_DISALLOWED_APP" /> <protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_DISMISSED" /> - <protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_ALLOWED_CARRIER" /> - <protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_DISALLOWED_CARRIER" /> + <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_ALLOWED_CARRIER" /> + <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_DISALLOWED_CARRIER" /> + <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_DISMISSED" /> <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION" /> <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK" /> <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK" /> @@ -5457,6 +5458,13 @@ </intent-filter> </service> + <provider + android:name="com.android.server.textclassifier.IconsContentProvider" + android:authorities="com.android.textclassifier.icons" + android:enabled="true" + android:exported="true"> + </provider> + </application> </manifest> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 19ad6f717ea5..63a6b4088abb 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -883,6 +883,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "-242787066": { + "message": "addTaskToRecentsAnimationIfNeeded, control: %s, task: %s, transit: %s", + "level": "DEBUG", + "group": "WM_DEBUG_RECENTS_ANIMATIONS", + "at": "com\/android\/server\/wm\/WindowContainer.java" + }, "-198463978": { "message": "updateRotationUnchecked: alwaysSendConfiguration=%b forceRelayout=%b", "level": "VERBOSE", @@ -901,6 +907,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/ScreenRotationAnimation.java" }, + "-172900257": { + "message": "addTaskToTargets, target: %s", + "level": "DEBUG", + "group": "WM_DEBUG_RECENTS_ANIMATIONS", + "at": "com\/android\/server\/wm\/RecentsAnimationController.java" + }, "-167822951": { "message": "Attempted to add starting window to token with already existing starting window", "level": "WARN", @@ -1525,6 +1537,12 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimation.java" }, + "854237232": { + "message": "addTaskToRecentsAnimationIfNeeded, control: %s, task: %s, transit: %s", + "level": "DEBUG", + "group": "WM_DEBUG_RECENTS_ANIMATIONS", + "at": "com\/android\/server\/wm\/Task.java" + }, "873914452": { "message": "goodToGo()", "level": "DEBUG", diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarTrustAgentUnlockDialogHelper.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarTrustAgentUnlockDialogHelper.java deleted file mode 100644 index 597716569e9b..000000000000 --- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/CarTrustAgentUnlockDialogHelper.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.car.userswitcher; - -import android.app.admin.DevicePolicyManager; -import android.bluetooth.BluetoothAdapter; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.Resources; -import android.graphics.PixelFormat; -import android.os.Handler; -import android.os.UserHandle; -import android.os.UserManager; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.TextView; - -import com.android.internal.widget.LockPatternUtils; -import com.android.systemui.R; -import com.android.systemui.dagger.qualifiers.Main; - -import javax.inject.Inject; -import javax.inject.Singleton; - -/** - * A helper class displays an unlock dialog and receives broadcast about detecting trusted device - * & unlocking state to show the appropriate message on the dialog. - */ -@Singleton -class CarTrustAgentUnlockDialogHelper extends BroadcastReceiver{ - private static final String TAG = CarTrustAgentUnlockDialogHelper.class.getSimpleName(); - - private final Context mContext; - private final Resources mResources; - private final WindowManager mWindowManager; - private final UserManager mUserManager; - private final WindowManager.LayoutParams mParams; - /** - * Not using Dialog because context passed from {@link FullscreenUserSwitcherViewMediator} - * is not an activity. - */ - private final View mUnlockDialogLayout; - private final TextView mUnlockingText; - private final Button mButton; - private final IntentFilter mFilter; - private int mUid; - private boolean mIsDialogShowing; - private OnHideListener mOnHideListener; - - @Inject - CarTrustAgentUnlockDialogHelper(Context context, @Main Resources resources, - UserManager userManager, WindowManager windowManager) { - mContext = context; - mResources = resources; - mUserManager = userManager; - mWindowManager = windowManager; - mParams = createLayoutParams(); - mFilter = getIntentFilter(); - - mParams.packageName = mContext.getPackageName(); - mParams.setTitle(mContext.getString(R.string.unlock_dialog_title)); - - mUnlockDialogLayout = LayoutInflater.from(mContext).inflate( - R.layout.trust_agent_unlock_dialog, null); - mUnlockDialogLayout.setLayoutParams(mParams); - - View dialogParent = mUnlockDialogLayout.findViewById(R.id.unlock_dialog_parent); - dialogParent.setOnTouchListener((v, event)-> { - hideUnlockDialog(/* dismissUserSwitcher= */ false); - return true; - }); - View unlockDialog = mUnlockDialogLayout.findViewById(R.id.unlock_dialog); - unlockDialog.setOnTouchListener((v, event) -> { - // If the person taps inside the unlock dialog, the touch event will be intercepted here - // and the dialog will not exit - return true; - }); - mUnlockingText = mUnlockDialogLayout.findViewById(R.id.unlocking_text); - mButton = mUnlockDialogLayout.findViewById(R.id.enter_pin_button); - mButton.setOnClickListener(v -> { - hideUnlockDialog(/* dismissUserSwitcher= */true); - // TODO(b/138250105) Stop unlock advertising - }); - - BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - if (bluetoothAdapter != null - && bluetoothAdapter.getLeState() == BluetoothAdapter.STATE_BLE_ON) { - mUnlockingText.setText(R.string.unlock_dialog_message_start); - } - } - - /** - * This filter is listening on: - * {@link BluetoothAdapter#ACTION_BLE_STATE_CHANGED} for starting unlock advertising; - * {@link Intent#ACTION_USER_UNLOCKED} for IHU unlocked - */ - private IntentFilter getIntentFilter() { - IntentFilter filter = new IntentFilter(); - filter.addAction(BluetoothAdapter.ACTION_BLE_STATE_CHANGED); - filter.addAction(Intent.ACTION_USER_UNLOCKED); - return filter; - } - - /** - * Show dialog for the given user - */ - void showUnlockDialog(int uid, OnHideListener listener) { - showUnlockDialogAfterDelay(uid, 0, listener); - } - - /** - * Show dialog for the given user after the certain time of delay has elapsed - * - * @param uid the user to unlock - * @param listener listener that listens to dialog hide - */ - void showUnlockDialogAfterDelay(int uid, OnHideListener listener) { - long delayMillis = mResources.getInteger(R.integer.unlock_dialog_delay_ms); - showUnlockDialogAfterDelay(uid, delayMillis, listener); - } - - /** - * Show dialog for the given user after the supplied delay has elapsed - */ - private void showUnlockDialogAfterDelay(int uid, long delayMillis, OnHideListener listener) { - setUid(uid); - mOnHideListener = listener; - if (!mIsDialogShowing) { - logd("Receiver registered"); - mContext.registerReceiverAsUser(this, UserHandle.ALL, mFilter, - /* broadcastPermission= */ null, - /* scheduler= */ null); - new Handler().postDelayed(() -> { - if (!mUserManager.isUserUnlocked(uid)) { - logd("Showed unlock dialog for user: " + uid + " after " + delayMillis - + " delay."); - mWindowManager.addView(mUnlockDialogLayout, mParams); - } - }, delayMillis); - } - mIsDialogShowing = true; - } - - private void setUid(int uid) { - mUid = uid; - TextView userName = mUnlockDialogLayout.findViewById(R.id.user_name); - userName.setText(mUserManager.getUserInfo(mUid).name); - ImageView avatar = mUnlockDialogLayout.findViewById(R.id.avatar); - avatar.setImageBitmap(mUserManager.getUserIcon(mUid)); - setButtonText(); - } - - private void hideUnlockDialog(boolean dismissUserSwitcher) { - if (!mIsDialogShowing) { - return; - } - mWindowManager.removeView(mUnlockDialogLayout); - logd("Receiver unregistered"); - mContext.unregisterReceiver(this); - if (mOnHideListener != null) { - mOnHideListener.onHide(dismissUserSwitcher); - } - mIsDialogShowing = false; - } - - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action == null) { - return; - } - switch (action) { - case BluetoothAdapter.ACTION_BLE_STATE_CHANGED: - logd("Received ACTION_BLE_STATE_CHANGED"); - int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); - if (state == BluetoothAdapter.STATE_BLE_ON) { - logd("Received BLE_ON"); - mUnlockingText.setText(R.string.unlock_dialog_message_start); - } - break; - case Intent.ACTION_USER_UNLOCKED: - int uid = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - if (uid == mUid) { - logd("IHU unlocked"); - hideUnlockDialog(/* notifyOnHideListener= */false); - } else { - Log.e(TAG, "Received ACTION_USER_UNLOCKED for unexpected uid: " + uid); - } - break; - default: - Log.e(TAG, "Encountered unexpected action when attempting to set " - + "unlock state message: " + action); - } - } - - // Set button text based on screen lock type - private void setButtonText() { - LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext); - int passwordQuality = lockPatternUtils.getActivePasswordQuality(mUid); - switch (passwordQuality) { - // PIN - case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: - case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: - mButton.setText(R.string.unlock_dialog_button_text_pin); - break; - // Pattern - case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: - mButton.setText(R.string.unlock_dialog_button_text_pattern); - break; - // Password - case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: - case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: - case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: - mButton.setText(R.string.unlock_dialog_button_text_password); - break; - default: - Log.e(TAG, "Encountered unexpected screen lock type when attempting to set " - + "button text:" + passwordQuality); - } - } - - private WindowManager.LayoutParams createLayoutParams() { - final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( - WindowManager.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, - WindowManager.LayoutParams.FLAG_FULLSCREEN - | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS - | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, - PixelFormat.TRANSLUCENT - ); - attrs.setFitInsetsTypes(0 /* types */); - return attrs; - } - - private void logd(String message) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, message); - } - } - - /** - * Listener used to notify when the dialog is hidden - */ - interface OnHideListener { - void onHide(boolean dismissUserSwitcher); - } -} diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java index 627729768e88..50e43bea65eb 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java @@ -16,23 +16,14 @@ package com.android.systemui.car.userswitcher; -import android.car.Car; -import android.car.trust.CarTrustAgentEnrollmentManager; -import android.car.userlib.CarUserManagerHelper; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.UserInfo; import android.content.res.Resources; import android.os.Handler; -import android.os.UserHandle; -import android.os.UserManager; -import android.util.Log; -import com.android.internal.widget.LockPatternUtils; import com.android.systemui.R; -import com.android.systemui.car.CarServiceProvider; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -52,9 +43,6 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator { private static final String TAG = FullscreenUserSwitcherViewMediator.class.getSimpleName(); private final Context mContext; - private final UserManager mUserManager; - private final CarServiceProvider mCarServiceProvider; - private final CarTrustAgentUnlockDialogHelper mUnlockDialogHelper; private final CarStatusBarKeyguardViewManager mCarStatusBarKeyguardViewManager; private final Handler mMainHandler; private final StatusBarStateController mStatusBarStateController; @@ -62,39 +50,12 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator { private final ScreenLifecycle mScreenLifecycle; private final CarStatusBar mCarStatusBar; private final boolean mIsUserSwitcherEnabled; - private final CarUserManagerHelper mCarUserManagerHelper; - - private CarTrustAgentEnrollmentManager mEnrollmentManager; - private UserGridRecyclerView.UserRecord mSelectedUser; - private final CarTrustAgentUnlockDialogHelper.OnHideListener mOnHideListener = - dismissUserSwitcher -> { - if (dismissUserSwitcher) { - dismissUserSwitcher(); - } else { - // Re-draw the parent view, otherwise the unlock dialog will not be removed - // from the screen immediately. - invalidateFullscreenUserSwitcherView(); - } - }; - private final BroadcastReceiver mUserUnlockReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "user 0 is unlocked, SharedPreference is accessible."); - } - showDialogForInitialUser(); - mContext.unregisterReceiver(mUserUnlockReceiver); - } - }; @Inject public FullscreenUserSwitcherViewMediator( Context context, @Main Resources resources, @Main Handler mainHandler, - UserManager userManager, - CarServiceProvider carServiceProvider, - CarTrustAgentUnlockDialogHelper carTrustAgentUnlockDialogHelper, CarStatusBarKeyguardViewManager carStatusBarKeyguardViewManager, CarStatusBar carStatusBar, StatusBarStateController statusBarStateController, @@ -105,21 +66,12 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator { mIsUserSwitcherEnabled = resources.getBoolean(R.bool.config_enableFullscreenUserSwitcher); mMainHandler = mainHandler; - mUserManager = userManager; - mCarServiceProvider = carServiceProvider; - mCarServiceProvider.addListener( - car -> mEnrollmentManager = (CarTrustAgentEnrollmentManager) car.getCarManager( - Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE)); - - mUnlockDialogHelper = carTrustAgentUnlockDialogHelper; mCarStatusBarKeyguardViewManager = carStatusBarKeyguardViewManager; mCarStatusBar = carStatusBar; mStatusBarStateController = statusBarStateController; mFullScreenUserSwitcherViewController = fullScreenUserSwitcherViewController; mScreenLifecycle = screenLifecycle; - - mCarUserManagerHelper = new CarUserManagerHelper(mContext); } @Override @@ -127,18 +79,6 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator { registerUserSwitcherShowListeners(); registerUserSwitcherHideListeners(); registerHideKeyguardListeners(); - - if (mUserManager.isUserUnlocked(UserHandle.USER_SYSTEM)) { - // User0 is unlocked, switched to the initial user - showDialogForInitialUser(); - } else { - // listen to USER_UNLOCKED - mContext.registerReceiverAsUser(mUserUnlockReceiver, - UserHandle.getUserHandleForUid(UserHandle.USER_SYSTEM), - new IntentFilter(Intent.ACTION_USER_UNLOCKED), - /* broadcastPermission= */ null, - /* scheduler= */ null); - } } private void registerUserSwitcherShowListeners() { @@ -194,26 +134,16 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator { } /** - * Every time user clicks on an item in the switcher, if the clicked user has no trusted - * device, we hide the switcher, either gradually or immediately. - * If the user has trusted device, we show an unlock dialog to notify user the unlock - * state. - * When the unlock dialog is dismissed by user, we hide the unlock dialog and the switcher. - * We dismiss the entire keyguard when we hide the switcher if user clicked on the - * foreground user (user we're already logged in as). + * Every time user clicks on an item in the switcher, we hide the switcher. + * + * We dismiss the entire keyguard if user clicked on the foreground user (user we're already + * logged in as). */ private void onUserSelected(UserGridRecyclerView.UserRecord record) { - mSelectedUser = record; - if (record.mInfo != null) { - if (hasScreenLock(record.mInfo.id) && hasTrustedDevice(record.mInfo.id)) { - mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, mOnHideListener); - return; - } - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "no trusted device enrolled for uid: " + record.mInfo.id); - } + hide(); + if (record.mType == UserGridRecyclerView.UserRecord.FOREGROUND_USER) { + mCarStatusBar.dismissKeyguard(); } - dismissUserSwitcher(); } // We automatically dismiss keyguard unless user switcher is being shown above the keyguard. @@ -234,53 +164,6 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator { } } - private boolean hasScreenLock(int uid) { - LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext); - return lockPatternUtils.getCredentialTypeForUser(uid) - != LockPatternUtils.CREDENTIAL_TYPE_NONE; - } - - private boolean hasTrustedDevice(int uid) { - if (mEnrollmentManager == null) { // car service not ready, so it cannot be available. - return false; - } - return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty(); - } - - private void dismissUserSwitcher() { - if (mSelectedUser == null) { - Log.e(TAG, "Request to dismiss user switcher, but no user selected"); - return; - } - if (mSelectedUser.mType == UserGridRecyclerView.UserRecord.FOREGROUND_USER) { - hide(); - mCarStatusBar.dismissKeyguard(); - return; - } - hide(); - } - - private void showDialogForInitialUser() { - int initialUser = mCarUserManagerHelper.getInitialUser(); - UserInfo initialUserInfo = mUserManager.getUserInfo(initialUser); - mSelectedUser = new UserGridRecyclerView.UserRecord(initialUserInfo, - UserGridRecyclerView.UserRecord.FOREGROUND_USER); - - // If the initial user has screen lock and trusted device, display the unlock dialog on the - // keyguard. - if (hasScreenLock(initialUser) && hasTrustedDevice(initialUser)) { - mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser, - mOnHideListener); - } else { - // If no trusted device, dismiss the keyguard. - dismissUserSwitcher(); - } - } - - private void invalidateFullscreenUserSwitcherView() { - mFullScreenUserSwitcherViewController.invalidate(); - } - private void hide() { mFullScreenUserSwitcherViewController.stop(); } diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 53e7921879d1..1007d8379b8e 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -646,6 +646,8 @@ <string name="wifi_verbose_logging">Enable Wi\u2011Fi Verbose Logging</string> <!-- Setting Checkbox title whether to disable WiFi Scan Throttling. [CHAR LIMIT=40] --> <string name="wifi_scan_throttling">Wi\u2011Fi scan throttling</string> + <!-- Setting Checkbox title whether to enable WiFi enhanced mac randomization. [CHAR LIMIT=40] --> + <string name="wifi_enhanced_mac_randomization">Wi\u2011Fi\u2011enhanced MAC randomization</string> <!-- Setting Checkbox title whether to always keep mobile data active. [CHAR LIMIT=80] --> <string name="mobile_data_always_on">Mobile data always active</string> <!-- Setting Checkbox title whether to enable hardware acceleration for tethering. [CHAR LIMIT=80] --> @@ -716,6 +718,8 @@ <string name="wifi_verbose_logging_summary">Increase Wi\u2011Fi logging level, show per SSID RSSI in Wi\u2011Fi Picker</string> <!-- Setting Checkbox summary whether to disable Wifi scan throttling [CHAR LIMIT=NONE] --> <string name="wifi_scan_throttling_summary">Reduces battery drain & improves network performance</string> + <!-- Setting Checkbox title whether to enable WiFi enhanced mac randomization. [CHAR LIMIT=NONE] --> + <string name="wifi_enhanced_mac_randomization_summary">This toggle affects MAC randomization behavior for client mode only.\nWhen this mode is activated, any networks that have MAC randomization enabled may have their MAC addresses re\u2011randomized during association, depending on when the client last disconnected from the network. Re\u2011randomization does not occur if the device reconnects in 4 hours or less.</string> <!-- Label indicating network has been manually marked as metered --> <string name="wifi_metered_label">Metered</string> <!-- Label indicating network has been manually marked as unmetered --> diff --git a/packages/SystemUI/res/drawable/ic_device_air_purifier_off.xml b/packages/SystemUI/res/drawable/ic_device_air_purifier_off.xml index b18c3e77ef10..021c3eee50df 100644 --- a/packages/SystemUI/res/drawable/ic_device_air_purifier_off.xml +++ b/packages/SystemUI/res/drawable/ic_device_air_purifier_off.xml @@ -20,15 +20,15 @@ android:viewportWidth="24" android:viewportHeight="24"> <path - android:pathData="M16,19H5V7C5.0016,6.47 5.2128,5.9623 5.5875,5.5875C5.9623,5.2128 6.47,5.0016 7,5H14C14.5299,5.0016 15.0377,5.2128 15.4125,5.5875C15.7872,5.9623 15.9984,6.47 16,7V8H18V7C18,5.9391 17.5786,4.9217 16.8284,4.1716C16.0783,3.4214 15.0609,3 14,3H7C5.9391,3 4.9217,3.4214 4.1716,4.1716C3.4214,4.9217 3,5.9391 3,7V21H18V17H16V19Z" + android:pathData="M10,3H6C5.2043,3 4.4413,3.3161 3.8787,3.8787C3.3161,4.4413 3,5.2043 3,6V21H13V6C13,5.2043 12.6839,4.4413 12.1213,3.8787C11.5587,3.3161 10.7956,3 10,3ZM11,15H10V10H11V15ZM10,8C9.4696,8 8.9609,8.2107 8.5858,8.5858C8.2107,8.9609 8,9.4696 8,10V15C8,15.5304 8.2107,16.0391 8.5858,16.4142C8.9609,16.7893 9.4696,17 10,17H11V19H5V6C5,5.7348 5.1054,5.4804 5.2929,5.2929C5.4804,5.1054 5.7348,5 6,5H10C10.2652,5 10.5196,5.1054 10.7071,5.2929C10.8946,5.4804 11,5.7348 11,6V8H10Z" android:fillColor="#FF000000" /> <path - android:pathData="M19,13C18.6049,13.378 18.1309,13.6638 17.6122,13.8367C17.0935,14.0096 16.5429,14.0653 16,14V16C16.5429,16.0653 17.0935,16.0096 17.6122,15.8367C18.1309,15.6638 18.6049,15.378 19,15C19.93,14.02 20,14 21,14V12C20,12 19.93,12.02 19,13Z" + android:pathData="M18.445,11.168C17.398,11.868 16.574,11.468 15.316,11.051L14.684,12.951C15.476,13.3014 16.3252,13.5047 17.19,13.551C18.0324,13.5503 18.8555,13.2994 19.555,12.83C20.596,12.135 21.383,12.514 22.684,12.947L23.316,11.047C22.688,10.842 20.57,9.753 18.445,11.168Z" android:fillColor="#FF000000" /> <path - android:pathData="M19,9C18.6049,9.378 18.1309,9.6638 17.6122,9.8367C17.0935,10.0096 16.5429,10.0653 16,10V12C16.5429,12.0653 17.0935,12.0096 17.6122,11.8367C18.1309,11.6638 18.6049,11.378 19,11C19.93,10.02 20,10 21,10V8C20,8 19.93,8.02 19,9Z" + android:pathData="M17.189,9.659C18.0316,9.658 18.855,9.4071 19.555,8.938C20.596,8.244 21.384,8.622 22.684,9.055L23.316,7.155C22.727,6.955 20.583,5.8489 18.445,7.2719C17.406,7.966 16.615,7.59 15.317,7.156L14.683,9.056C15.4748,9.4074 16.324,9.6117 17.189,9.659V9.659Z" android:fillColor="#FF000000" /> <path - android:pathData="M10.5,8C9.7089,8 8.9355,8.2346 8.2777,8.6741C7.6199,9.1137 7.1072,9.7383 6.8045,10.4692C6.5017,11.2001 6.4225,12.0044 6.5769,12.7803C6.7312,13.5563 7.1122,14.269 7.6716,14.8284C8.231,15.3878 8.9437,15.7688 9.7196,15.9232C10.4956,16.0775 11.2998,15.9982 12.0307,15.6955C12.7616,15.3927 13.3864,14.8801 13.8259,14.2223C14.2654,13.5645 14.5,12.7911 14.5,12C14.5,10.9391 14.0786,9.9217 13.3284,9.1716C12.5783,8.4214 11.5609,8 10.5,8ZM10.5,14C10.1044,14 9.7178,13.8827 9.3889,13.663C9.06,13.4432 8.8036,13.1308 8.6522,12.7654C8.5009,12.3999 8.4613,11.9978 8.5384,11.6098C8.6156,11.2218 8.8061,10.8655 9.0858,10.5858C9.3655,10.3061 9.7219,10.1156 10.1098,10.0385C10.4978,9.9613 10.8999,10.0008 11.2654,10.1522C11.6308,10.3036 11.9432,10.56 12.1629,10.8889C12.3827,11.2178 12.5,11.6044 12.5,12C12.5,12.5304 12.2893,13.0391 11.9142,13.4142C11.5391,13.7893 11.0304,14 10.5,14Z" + android:pathData="M18.445,15.063C17.41,15.752 16.634,15.384 15.316,14.945L14.684,16.845C15.4762,17.1948 16.3253,17.3981 17.19,17.445C18.0322,17.4438 18.8551,17.1932 19.555,16.725C20.594,16.031 21.385,16.407 22.683,16.841L23.317,14.941C22.728,14.749 20.583,13.637 18.445,15.063Z" android:fillColor="#FF000000" /> </vector> diff --git a/packages/SystemUI/res/drawable/ic_device_air_purifier_on.xml b/packages/SystemUI/res/drawable/ic_device_air_purifier_on.xml index b18c3e77ef10..9533cfe30b75 100644 --- a/packages/SystemUI/res/drawable/ic_device_air_purifier_on.xml +++ b/packages/SystemUI/res/drawable/ic_device_air_purifier_on.xml @@ -20,15 +20,18 @@ android:viewportWidth="24" android:viewportHeight="24"> <path - android:pathData="M16,19H5V7C5.0016,6.47 5.2128,5.9623 5.5875,5.5875C5.9623,5.2128 6.47,5.0016 7,5H14C14.5299,5.0016 15.0377,5.2128 15.4125,5.5875C15.7872,5.9623 15.9984,6.47 16,7V8H18V7C18,5.9391 17.5786,4.9217 16.8284,4.1716C16.0783,3.4214 15.0609,3 14,3H7C5.9391,3 4.9217,3.4214 4.1716,4.1716C3.4214,4.9217 3,5.9391 3,7V21H18V17H16V19Z" + android:pathData="M10,3H6C5.2043,3 4.4413,3.3161 3.8787,3.8787C3.3161,4.4413 3,5.2043 3,6V21H13V17H10C9.4696,17 8.9609,16.7893 8.5858,16.4142C8.2107,16.0391 8,15.5304 8,15V10C8,9.4696 8.2107,8.9609 8.5858,8.5858C8.9609,8.2107 9.4696,8 10,8H13V6C13,5.2043 12.6839,4.4413 12.1213,3.8787C11.5587,3.3161 10.7956,3 10,3Z" android:fillColor="#FF000000" /> <path - android:pathData="M19,13C18.6049,13.378 18.1309,13.6638 17.6122,13.8367C17.0935,14.0096 16.5429,14.0653 16,14V16C16.5429,16.0653 17.0935,16.0096 17.6122,15.8367C18.1309,15.6638 18.6049,15.378 19,15C19.93,14.02 20,14 21,14V12C20,12 19.93,12.02 19,13Z" + android:pathData="M13,10H10V15H13V10Z" android:fillColor="#FF000000" /> <path - android:pathData="M19,9C18.6049,9.378 18.1309,9.6638 17.6122,9.8367C17.0935,10.0096 16.5429,10.0653 16,10V12C16.5429,12.0653 17.0935,12.0096 17.6122,11.8367C18.1309,11.6638 18.6049,11.378 19,11C19.93,10.02 20,10 21,10V8C20,8 19.93,8.02 19,9Z" + android:pathData="M18.445,11.168C17.398,11.868 16.574,11.468 15.316,11.051L14.684,12.951C15.476,13.3014 16.3252,13.5047 17.19,13.551C18.0324,13.5503 18.8555,13.2994 19.555,12.83C20.596,12.135 21.383,12.514 22.684,12.947L23.316,11.047C22.688,10.842 20.57,9.753 18.445,11.168Z" android:fillColor="#FF000000" /> <path - android:pathData="M10.5,8C9.7089,8 8.9355,8.2346 8.2777,8.6741C7.6199,9.1137 7.1072,9.7383 6.8045,10.4692C6.5017,11.2001 6.4225,12.0044 6.5769,12.7803C6.7312,13.5563 7.1122,14.269 7.6716,14.8284C8.231,15.3878 8.9437,15.7688 9.7196,15.9232C10.4956,16.0775 11.2998,15.9982 12.0307,15.6955C12.7616,15.3927 13.3864,14.8801 13.8259,14.2223C14.2654,13.5645 14.5,12.7911 14.5,12C14.5,10.9391 14.0786,9.9217 13.3284,9.1716C12.5783,8.4214 11.5609,8 10.5,8ZM10.5,14C10.1044,14 9.7178,13.8827 9.3889,13.663C9.06,13.4432 8.8036,13.1308 8.6522,12.7654C8.5009,12.3999 8.4613,11.9978 8.5384,11.6098C8.6156,11.2218 8.8061,10.8655 9.0858,10.5858C9.3655,10.3061 9.7219,10.1156 10.1098,10.0385C10.4978,9.9613 10.8999,10.0008 11.2654,10.1522C11.6308,10.3036 11.9432,10.56 12.1629,10.8889C12.3827,11.2178 12.5,11.6044 12.5,12C12.5,12.5304 12.2893,13.0391 11.9142,13.4142C11.5391,13.7893 11.0304,14 10.5,14Z" + android:pathData="M17.189,9.659C18.0316,9.658 18.855,9.4071 19.555,8.938C20.596,8.244 21.384,8.622 22.684,9.055L23.316,7.155C22.727,6.955 20.583,5.8489 18.445,7.2719C17.406,7.966 16.615,7.59 15.317,7.156L14.683,9.056C15.4748,9.4074 16.324,9.6117 17.189,9.659V9.659Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M18.445,15.063C17.41,15.752 16.634,15.384 15.316,14.945L14.684,16.845C15.4762,17.1948 16.3253,17.3981 17.19,17.445C18.0322,17.4438 18.8551,17.1932 19.555,16.725C20.594,16.031 21.385,16.407 22.683,16.841L23.317,14.941C22.728,14.749 20.583,13.637 18.445,15.063Z" android:fillColor="#FF000000" /> </vector> diff --git a/packages/SystemUI/res/drawable/ic_device_cooking_off.xml b/packages/SystemUI/res/drawable/ic_device_cooking_off.xml new file mode 100644 index 000000000000..272a3bb79d40 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_device_cooking_off.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M9,16H2V18H9V21H11V18C11,17.4696 10.7893,16.9609 10.4142,16.5858C10.0391,16.2107 9.5304,16 9,16Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M15,16C14.4696,16 13.9609,16.2107 13.5858,16.5858C13.2107,16.9609 13,17.4696 13,18V21H15V18H22V16H15Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M6,15H18C18.7956,15 19.5587,14.6839 20.1213,14.1213C20.6839,13.5587 21,12.7956 21,12V8H3V12C3,12.7956 3.3161,13.5587 3.8787,14.1213C4.4413,14.6839 5.2043,15 6,15ZM5,10H19V12C19,12.2652 18.8946,12.5196 18.7071,12.7072C18.5196,12.8947 18.2652,13 18,13H6C5.7348,13 5.4804,12.8947 5.2929,12.7072C5.1054,12.5196 5,12.2652 5,12V10Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M21,5H15V4C15,3.7348 14.8946,3.4804 14.7071,3.2929C14.5196,3.1053 14.2652,3 14,3H10C9.7348,3 9.4804,3.1053 9.2929,3.2929C9.1054,3.4804 9,3.7348 9,4V5H3V7H21V5Z" + android:fillColor="#FF000000" /> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_device_cooking_on.xml b/packages/SystemUI/res/drawable/ic_device_cooking_on.xml new file mode 100644 index 000000000000..3785f8bffc81 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_device_cooking_on.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M9,16H2V18H9V21H11V18C11,17.4696 10.7893,16.9609 10.4142,16.5858C10.0391,16.2107 9.5304,16 9,16Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M13,18V21H15V18H22V16H15C14.4696,16 13.9609,16.2107 13.5858,16.5858C13.2107,16.9609 13,17.4696 13,18Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M3,12C3,12.7956 3.3161,13.5587 3.8787,14.1213C4.4413,14.6839 5.2043,15 6,15H18C18.7956,15 19.5587,14.6839 20.1213,14.1213C20.6839,13.5587 21,12.7956 21,12V8H3V12Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M21,5H15V4C15,3.7348 14.8946,3.4804 14.7071,3.2929C14.5196,3.1053 14.2652,3 14,3H10C9.7348,3 9.4804,3.1053 9.2929,3.2929C9.1054,3.4804 9,3.7348 9,4V5H3V7H21V5Z" + android:fillColor="#FF000000" /> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_device_display_off.xml b/packages/SystemUI/res/drawable/ic_device_display_off.xml new file mode 100644 index 000000000000..07737c959339 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_device_display_off.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M20,3H4C3.4701,3.0016 2.9623,3.2129 2.5875,3.5877C2.2128,3.9624 2.0016,4.47 2,5V17C2.0016,17.5299 2.2128,18.0376 2.5875,18.4124C2.9623,18.7871 3.4701,18.9984 4,19H8V21H16V19H20C20.5287,18.9974 21.0348,18.7854 21.4078,18.4106C21.7807,18.0359 21.99,17.5287 21.99,17L22,5C21.9984,4.47 21.7872,3.9624 21.4125,3.5877C21.0377,3.2129 20.5299,3.0016 20,3V3ZM20,17H4V5H20V17Z" + android:fillColor="#FF000000" /> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_device_display_on.xml b/packages/SystemUI/res/drawable/ic_device_display_on.xml new file mode 100644 index 000000000000..2416f6bb4b83 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_device_display_on.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M20,3H4C3.4701,3.0016 2.9623,3.2129 2.5875,3.5877C2.2128,3.9624 2.0016,4.47 2,5V17C2.0016,17.5299 2.2128,18.0376 2.5875,18.4124C2.9623,18.7871 3.4701,18.9984 4,19H8V21H16V19H20C20.5287,18.9974 21.0348,18.7854 21.4078,18.4106C21.7807,18.0359 21.99,17.5287 21.99,17L22,5C21.9984,4.47 21.7872,3.9624 21.4125,3.5877C21.0377,3.2129 20.5299,3.0016 20,3V3ZM20,17H4V5H20V17Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M19,6H5V16H19V6Z" + android:fillColor="#FF000000" /> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_device_door_off.xml b/packages/SystemUI/res/drawable/ic_device_door_off.xml new file mode 100644 index 000000000000..291f312e18a6 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_device_door_off.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M7,14H5C4.7348,14 4.4804,14.1054 4.2929,14.293C4.1054,14.4805 4,14.7348 4,15C4,15.2652 4.1054,15.5195 4.2929,15.707C4.4804,15.8946 4.7348,16 5,16H7V21H17V3H7V14ZM15,5V19H9V16H12C12.2652,16 12.5196,15.8946 12.7071,15.707C12.8946,15.5195 13,15.2652 13,15C13,14.7348 12.8946,14.4805 12.7071,14.293C12.5196,14.1054 12.2652,14 12,14H9V5H15Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M20,3C19.7348,3 19.4804,3.1054 19.2929,3.293C19.1054,3.4805 19,3.7348 19,4V8C19,8.2652 19.1054,8.5195 19.2929,8.707C19.4804,8.8946 19.7348,9 20,9C20.2652,9 20.5196,8.8946 20.7071,8.707C20.8946,8.5195 21,8.2652 21,8V4C21,3.7348 20.8946,3.4805 20.7071,3.293C20.5196,3.1054 20.2652,3 20,3Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M12,10C12.5523,10 13,9.5523 13,9C13,8.4477 12.5523,8 12,8C11.4477,8 11,8.4477 11,9C11,9.5523 11.4477,10 12,10Z" + android:fillColor="#FF000000" /> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_device_door_on.xml b/packages/SystemUI/res/drawable/ic_device_door_on.xml new file mode 100644 index 000000000000..e6cdf11f8dea --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_device_door_on.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M20,3C19.7348,3 19.4804,3.1054 19.2929,3.293C19.1054,3.4805 19,3.7348 19,4V8C19,8.2652 19.1054,8.5195 19.2929,8.707C19.4804,8.8946 19.7348,9 20,9C20.2652,9 20.5196,8.8946 20.7071,8.707C20.8946,8.5195 21,8.2652 21,8V4C21,3.7348 20.8946,3.4805 20.7071,3.293C20.5196,3.1054 20.2652,3 20,3Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M13,15C13,14.7348 12.8946,14.4805 12.7071,14.293C12.5196,14.1054 12.2652,14 12,14H5C4.7348,14 4.4804,14.1054 4.2929,14.293C4.1054,14.4805 4,14.7348 4,15C4,15.2652 4.1054,15.5195 4.2929,15.707C4.4804,15.8946 4.7348,16 5,16H12C12.2652,16 12.5196,15.8946 12.7071,15.707C12.8946,15.5195 13,15.2652 13,15Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M7,13H12C12.5304,13 13.0391,13.2109 13.4142,13.5859C13.7893,13.961 14,14.4696 14,15C14,15.5304 13.7893,16.039 13.4142,16.4141C13.0391,16.7891 12.5304,17 12,17H7V21H17V3H7V13ZM12,8C12.1978,8 12.3911,8.0586 12.5556,8.1685C12.72,8.2783 12.8482,8.4345 12.9239,8.6172C12.9996,8.7999 13.0194,9.0013 12.9808,9.1953C12.9422,9.3893 12.847,9.5672 12.7071,9.707C12.5673,9.8469 12.3891,9.9424 12.1951,9.981C12.0011,10.0195 11.8,9.9995 11.6173,9.9238C11.4346,9.8481 11.2784,9.7201 11.1685,9.5557C11.0586,9.3912 11,9.1978 11,9C11,8.7348 11.1054,8.4805 11.2929,8.293C11.4804,8.1054 11.7348,8 12,8Z" + android:fillColor="#FF000000" /> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_device_garage_on.xml b/packages/SystemUI/res/drawable/ic_device_garage_on.xml index 8865983f0d7d..eeb4bc153c5d 100644 --- a/packages/SystemUI/res/drawable/ic_device_garage_on.xml +++ b/packages/SystemUI/res/drawable/ic_device_garage_on.xml @@ -20,9 +20,12 @@ android:viewportWidth="24" android:viewportHeight="24"> <path - android:pathData="M20,9L12,3L4,9V21H6V10L12,5.5L18,10V21H20V9Z" + android:pathData="M12,3L4,9V21H7V11H17V21H20V9L12,3Z" android:fillColor="#FF000000" /> <path - android:pathData="M7,11V21H17V11H7ZM15,13V15H9V13H15ZM9,19V17H15V19H9Z" + android:pathData="M15,13H9V15H15V13Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M15,17H9V19H15V17Z" android:fillColor="#FF000000" /> </vector> diff --git a/packages/SystemUI/res/drawable/ic_device_outdoor_garden_off.xml b/packages/SystemUI/res/drawable/ic_device_outdoor_garden_off.xml new file mode 100644 index 000000000000..0d98f9e2a6f3 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_device_outdoor_garden_off.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M18,3L15,5.25L12,3L9,5.25L6,3L2,6V21H22V6L18,3ZM8,19H4V7L6,5.5L8,7V19ZM14,19H10V7L12,5.5L14,7V19ZM20,19H16V7L18,5.5L20,7V19Z" + android:fillColor="#FF000000" /> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_device_outdoor_garden_on.xml b/packages/SystemUI/res/drawable/ic_device_outdoor_garden_on.xml new file mode 100644 index 000000000000..00b6af68272a --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_device_outdoor_garden_on.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M2,6V21H8V6L5,3L2,6Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M9,6V21H15V6L12,3L9,6Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M19,3L16,6V21H22V6L19,3Z" + android:fillColor="#FF000000" /> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_device_pergola_on.xml b/packages/SystemUI/res/drawable/ic_device_pergola_on.xml index b7113dcd042f..cbbee8c05e1b 100644 --- a/packages/SystemUI/res/drawable/ic_device_pergola_on.xml +++ b/packages/SystemUI/res/drawable/ic_device_pergola_on.xml @@ -20,7 +20,7 @@ android:viewportWidth="24" android:viewportHeight="24"> <path - android:pathData="M20,2C19.7348,2 19.4804,2.1054 19.2929,2.293C19.1054,2.4805 19,2.7348 19,3V4H5V3C5,2.7348 4.8946,2.4805 4.7071,2.293C4.5196,2.1054 4.2652,2 4,2C3.7348,2 3.4804,2.1054 3.2929,2.293C3.1054,2.4805 3,2.7348 3,3V21H5V10H19V21H21V3C21,2.7348 20.8946,2.4805 20.7071,2.293C20.5196,2.1054 20.2652,2 20,2ZM5,8V6H19V8H5Z" + android:pathData="M20,2C19.7348,2 19.4804,2.1054 19.2929,2.293C19.1054,2.4805 19,2.7348 19,3V4H5V3C5,2.7348 4.8946,2.4805 4.7071,2.293C4.5196,2.1054 4.2652,2 4,2C3.7348,2 3.4804,2.1054 3.2929,2.293C3.1054,2.4805 3,2.7348 3,3V21H5V10H19V21H21V3C21,2.7348 20.8946,2.4805 20.7071,2.293C20.5196,2.1054 20.2652,2 20,2Z" android:fillColor="#FF000000" /> <path android:pathData="M8,18H11V21H13V18H16V16H8V18Z" diff --git a/packages/SystemUI/res/drawable/ic_device_thermostat_off.xml b/packages/SystemUI/res/drawable/ic_device_thermostat_off.xml index 1ba8741e4ae2..27da211b315d 100644 --- a/packages/SystemUI/res/drawable/ic_device_thermostat_off.xml +++ b/packages/SystemUI/res/drawable/ic_device_thermostat_off.xml @@ -20,6 +20,6 @@ android:viewportWidth="24" android:viewportHeight="24"> <path - android:pathData="M16,18.97C16.6469,18.1148 16.9979,17.0723 17,16C16.9993,15.2239 16.8183,14.4586 16.4712,13.7644C16.1241,13.0702 15.6205,12.4662 15,12V6C15,5.2043 14.6839,4.4413 14.1213,3.8787C13.5587,3.3161 12.7956,3 12,3C11.2044,3 10.4413,3.3161 9.8787,3.8787C9.3161,4.4413 9,5.2043 9,6V12C8.3795,12.4662 7.8759,13.0702 7.5288,13.7644C7.1817,14.4586 7.0007,15.2239 7,16C7.0021,17.0723 7.3531,18.1148 8,18.97V19H8.02C8.4815,19.6206 9.0818,20.1246 9.7729,20.4719C10.4639,20.8192 11.2266,21.0001 12,21.0001C12.7734,21.0001 13.5361,20.8192 14.2271,20.4719C14.9182,20.1246 15.5185,19.6206 15.98,19H16V18.97ZM10.2,13.6L11,13V6C11,5.7348 11.1054,5.4804 11.2929,5.2929C11.4804,5.1054 11.7348,5 12,5C12.2652,5 12.5196,5.1054 12.7071,5.2929C12.8946,5.4804 13,5.7348 13,6V13L13.8,13.6C14.1711,13.8809 14.4723,14.2435 14.6805,14.6598C14.8886,15.076 14.9979,15.5346 15,16H9C9.0009,15.5344 9.1098,15.0754 9.318,14.659C9.5262,14.2426 9.8281,13.8801 10.2,13.6Z" + android:pathData="M15,12V6C15,5.2043 14.6839,4.4413 14.1213,3.8787C13.5587,3.3161 12.7956,3 12,3C11.2044,3 10.4413,3.3161 9.8787,3.8787C9.3161,4.4413 9,5.2043 9,6V12C8.3795,12.4662 7.8759,13.0702 7.5288,13.7644C7.1817,14.4586 7.0007,15.2239 7,16C7.0021,17.0723 7.3531,18.1148 8,18.97V19H8.02C8.4815,19.6206 9.0818,20.1246 9.7729,20.4719C10.4639,20.8192 11.2266,21.0001 12,21.0001C12.7734,21.0001 13.5361,20.8192 14.2271,20.4719C14.9182,20.1246 15.5185,19.6206 15.98,19H16V18.97C16.6469,18.1148 16.9979,17.0723 17,16C16.9993,15.2239 16.8183,14.4586 16.4712,13.7644C16.1241,13.0702 15.6205,12.4662 15,12ZM14.4,17.773L14.367,17.819C14.0952,18.1807 13.744,18.4752 13.3405,18.6799C12.937,18.8846 12.4919,18.9941 12.0395,18.9998C11.5871,19.0055 11.1394,18.9073 10.7309,18.7128C10.3223,18.5184 9.9638,18.2327 9.683,17.878L9.604,17.778C9.2154,17.2664 9.0034,16.6425 9,16C9.0016,15.5346 9.1108,15.0758 9.3189,14.6595C9.5271,14.2432 9.8286,13.8805 10.2,13.6L11,13V6C11,5.7348 11.1054,5.4804 11.2929,5.2929C11.4804,5.1054 11.7348,5 12,5C12.2652,5 12.5196,5.1054 12.7071,5.2929C12.8946,5.4804 13,5.7348 13,6V13L13.8,13.6C14.1714,13.8805 14.4729,14.2432 14.6811,14.6595C14.8892,15.0758 14.9984,15.5346 15,16C14.9967,16.6403 14.7862,17.2623 14.4,17.773V17.773Z" android:fillColor="#FF000000" /> </vector> diff --git a/packages/SystemUI/res/drawable/ic_device_thermostat_on.xml b/packages/SystemUI/res/drawable/ic_device_thermostat_on.xml index 1ba8741e4ae2..deabb0e45b3d 100644 --- a/packages/SystemUI/res/drawable/ic_device_thermostat_on.xml +++ b/packages/SystemUI/res/drawable/ic_device_thermostat_on.xml @@ -20,6 +20,6 @@ android:viewportWidth="24" android:viewportHeight="24"> <path - android:pathData="M16,18.97C16.6469,18.1148 16.9979,17.0723 17,16C16.9993,15.2239 16.8183,14.4586 16.4712,13.7644C16.1241,13.0702 15.6205,12.4662 15,12V6C15,5.2043 14.6839,4.4413 14.1213,3.8787C13.5587,3.3161 12.7956,3 12,3C11.2044,3 10.4413,3.3161 9.8787,3.8787C9.3161,4.4413 9,5.2043 9,6V12C8.3795,12.4662 7.8759,13.0702 7.5288,13.7644C7.1817,14.4586 7.0007,15.2239 7,16C7.0021,17.0723 7.3531,18.1148 8,18.97V19H8.02C8.4815,19.6206 9.0818,20.1246 9.7729,20.4719C10.4639,20.8192 11.2266,21.0001 12,21.0001C12.7734,21.0001 13.5361,20.8192 14.2271,20.4719C14.9182,20.1246 15.5185,19.6206 15.98,19H16V18.97ZM10.2,13.6L11,13V6C11,5.7348 11.1054,5.4804 11.2929,5.2929C11.4804,5.1054 11.7348,5 12,5C12.2652,5 12.5196,5.1054 12.7071,5.2929C12.8946,5.4804 13,5.7348 13,6V13L13.8,13.6C14.1711,13.8809 14.4723,14.2435 14.6805,14.6598C14.8886,15.076 14.9979,15.5346 15,16H9C9.0009,15.5344 9.1098,15.0754 9.318,14.659C9.5262,14.2426 9.8281,13.8801 10.2,13.6Z" + android:pathData="M15,12V6C15,5.2043 14.6839,4.4413 14.1213,3.8787C13.5587,3.3161 12.7956,3 12,3C11.2044,3 10.4413,3.3161 9.8787,3.8787C9.3161,4.4413 9,5.2043 9,6V12C8.3795,12.4662 7.8759,13.0702 7.5288,13.7644C7.1817,14.4586 7.0007,15.2239 7,16C7.0021,17.0723 7.3531,18.1148 8,18.97V19H8.02C8.4815,19.6206 9.0818,20.1246 9.7729,20.4719C10.4639,20.8192 11.2266,21.0001 12,21.0001C12.7734,21.0001 13.5361,20.8192 14.2271,20.4719C14.9182,20.1246 15.5185,19.6206 15.98,19H16V18.97C16.6469,18.1148 16.9979,17.0723 17,16C16.9993,15.2239 16.8183,14.4586 16.4712,13.7644C16.1241,13.0702 15.6205,12.4662 15,12V12ZM11,6C11,5.7348 11.1054,5.4804 11.2929,5.2929C11.4804,5.1054 11.7348,5 12,5C12.2652,5 12.5196,5.1054 12.7071,5.2929C12.8946,5.4804 13,5.7348 13,6V10H11V6Z" android:fillColor="#FF000000" /> </vector> diff --git a/packages/SystemUI/res/drawable/ic_device_tv_off.xml b/packages/SystemUI/res/drawable/ic_device_tv_off.xml index dd91ed831bb8..f0c9b565acd6 100644 --- a/packages/SystemUI/res/drawable/ic_device_tv_off.xml +++ b/packages/SystemUI/res/drawable/ic_device_tv_off.xml @@ -20,6 +20,6 @@ android:viewportWidth="24" android:viewportHeight="24"> <path - android:pathData="M20,4H4C3.4701,4.0016 2.9623,4.2129 2.5875,4.5876C2.2128,4.9624 2.0016,5.47 2,6V17C2.0016,17.5299 2.2128,18.0376 2.5875,18.4124C2.9623,18.7871 3.4701,18.9984 4,19V21H5L5.667,19H18.333L19,21H20V19C20.5299,18.9984 21.0377,18.7871 21.4125,18.4124C21.7872,18.0376 21.9984,17.5299 22,17V6C21.9984,5.47 21.7872,4.9624 21.4125,4.5876C21.0377,4.2129 20.5299,4.0016 20,4ZM20,17H4V6H20V17Z" + android:pathData="M20,4H4C3.4701,4.0016 2.9623,4.2129 2.5875,4.5876C2.2128,4.9624 2.0016,5.47 2,6V17C2.0016,17.5299 2.2128,18.0376 2.5875,18.4124C2.9623,18.7871 3.4701,18.9984 4,19V21H5L5.667,19H18.333L19,21H20V19C20.5299,18.9984 21.0377,18.7871 21.4125,18.4124C21.7872,18.0376 21.9984,17.5299 22,17V6C21.9984,5.47 21.7872,4.9624 21.4125,4.5876C21.0377,4.2129 20.5299,4.0016 20,4V4ZM20,17H4V6H20V17Z" android:fillColor="#FF000000" /> </vector> diff --git a/packages/SystemUI/res/drawable/ic_device_tv_on.xml b/packages/SystemUI/res/drawable/ic_device_tv_on.xml index dd91ed831bb8..ed625e9420de 100644 --- a/packages/SystemUI/res/drawable/ic_device_tv_on.xml +++ b/packages/SystemUI/res/drawable/ic_device_tv_on.xml @@ -20,6 +20,9 @@ android:viewportWidth="24" android:viewportHeight="24"> <path - android:pathData="M20,4H4C3.4701,4.0016 2.9623,4.2129 2.5875,4.5876C2.2128,4.9624 2.0016,5.47 2,6V17C2.0016,17.5299 2.2128,18.0376 2.5875,18.4124C2.9623,18.7871 3.4701,18.9984 4,19V21H5L5.667,19H18.333L19,21H20V19C20.5299,18.9984 21.0377,18.7871 21.4125,18.4124C21.7872,18.0376 21.9984,17.5299 22,17V6C21.9984,5.47 21.7872,4.9624 21.4125,4.5876C21.0377,4.2129 20.5299,4.0016 20,4ZM20,17H4V6H20V17Z" + android:pathData="M20,4H4C3.4701,4.0016 2.9623,4.2129 2.5875,4.5876C2.2128,4.9624 2.0016,5.47 2,6V17C2.0016,17.5299 2.2128,18.0376 2.5875,18.4124C2.9623,18.7871 3.4701,18.9984 4,19V21H5L5.667,19H18.333L19,21H20V19C20.5299,18.9984 21.0377,18.7871 21.4125,18.4124C21.7872,18.0376 21.9984,17.5299 22,17V6C21.9984,5.47 21.7872,4.9624 21.4125,4.5876C21.0377,4.2129 20.5299,4.0016 20,4V4ZM20,17H4V6H20V17Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M19,7H5V16H19V7Z" android:fillColor="#FF000000" /> </vector> diff --git a/packages/SystemUI/res/drawable/ic_device_unknown_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_device_unknown_gm2_24px.xml deleted file mode 100644 index 24e063506250..000000000000 --- a/packages/SystemUI/res/drawable/ic_device_unknown_gm2_24px.xml +++ /dev/null @@ -1,9 +0,0 @@ -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24" - android:viewportHeight="24"> - <path - android:fillColor="#FF000000" - android:pathData="M12,2l-5.5,9h11L12,2zM12,5.84L13.93,9h-3.87L12,5.84zM17.5,13c-2.49,0 -4.5,2.01 -4.5,4.5s2.01,4.5 4.5,4.5 4.5,-2.01 4.5,-4.5 -2.01,-4.5 -4.5,-4.5zM17.5,20c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5zM3,21.5h8v-8L3,13.5v8zM5,15.5h4v4L5,19.5v-4z"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_device_unknown_off.xml b/packages/SystemUI/res/drawable/ic_device_unknown_off.xml new file mode 100644 index 000000000000..55820d0b5f63 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_device_unknown_off.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M8,6.5V11.76C8.8027,12.2963 9.4117,13.0766 9.7369,13.9856C10.0622,14.8946 10.0865,15.8841 9.8062,16.8079C9.526,17.7317 8.9561,18.541 8.1807,19.1161C7.4052,19.6912 6.4654,20.0017 5.5,20.0017C4.5346,20.0017 3.5948,19.6912 2.8193,19.1161C2.0439,18.541 1.474,17.7317 1.1938,16.8079C0.9135,15.8841 0.9378,14.8946 1.2631,13.9856C1.5883,13.0766 2.1973,12.2963 3,11.76V6.5C3,5.837 3.2634,5.2011 3.7322,4.7322C4.2011,4.2634 4.837,4 5.5,4C6.163,4 6.7989,4.2634 7.2678,4.7322C7.7366,5.2011 8,5.837 8,6.5ZM3.049,16H7.949C8.0467,15.5132 7.9978,15.0084 7.8084,14.5495C7.619,14.0905 7.2976,13.6981 6.885,13.422L5.998,12.828V6.5C5.998,6.3674 5.9453,6.2402 5.8516,6.1465C5.7578,6.0527 5.6306,6 5.498,6C5.3654,6 5.2382,6.0527 5.1445,6.1465C5.0507,6.2402 4.998,6.3674 4.998,6.5V12.828L4.111,13.422C3.7061,13.6922 3.3887,14.0746 3.1976,14.5223C3.0065,14.97 2.95,15.4637 3.035,15.943C3.0363,15.951 3.0389,15.959 3.0416,15.967C3.0453,15.978 3.049,15.989 3.049,16Z" + android:fillColor="#FF000000" + android:fillType="evenOdd"/> + <path + android:pathData="M14,16H20V18H14V16Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M17,4C15.7113,4.0006 14.457,4.416 13.4228,5.1849C12.3886,5.9538 11.6294,7.0353 11.2577,8.2692C10.8859,9.5031 10.9214,10.824 11.3587,12.0362C11.7961,13.2484 12.6121,14.2876 13.686,15H20.314C21.3879,14.2876 22.204,13.2484 22.6413,12.0362C23.0786,10.824 23.1141,9.5031 22.7423,8.2692C22.3706,7.0353 21.6114,5.9538 20.5772,5.1849C19.543,4.416 18.2887,4.0006 17,4ZM19.643,13H14.357C13.7469,12.4629 13.3149,11.7528 13.1185,10.9641C12.9221,10.1753 12.9707,9.3455 13.2577,8.5851C13.5447,7.8246 14.0566,7.1697 14.7251,6.7074C15.3937,6.2452 16.1872,5.9976 17,5.9976C17.8128,5.9976 18.6063,6.2452 19.2749,6.7074C19.9434,7.1697 20.4553,7.8246 20.7423,8.5851C21.0293,9.3455 21.0779,10.1753 20.8815,10.9641C20.6851,11.7528 20.2531,12.4629 19.643,13Z" + android:fillColor="#FF000000" + android:fillType="evenOdd"/> + <path + android:pathData="M18.0607,19.5607C17.7793,19.842 17.3978,20 17,20C16.6022,20 16.2207,19.842 15.9393,19.5607C15.658,19.2794 15.5,18.8978 15.5,18.5H18.5C18.5,18.8978 18.342,19.2794 18.0607,19.5607Z" + android:fillColor="#FF000000" /> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_device_unknown_on.xml b/packages/SystemUI/res/drawable/ic_device_unknown_on.xml new file mode 100644 index 000000000000..08d9817389d2 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_device_unknown_on.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M8,6.5V11.76C8.8027,12.2963 9.4117,13.0766 9.7369,13.9856C10.0622,14.8946 10.0865,15.8841 9.8062,16.8079C9.526,17.7317 8.9561,18.541 8.1807,19.1161C7.4052,19.6912 6.4654,20.0017 5.5,20.0017C4.5346,20.0017 3.5948,19.6912 2.8193,19.1161C2.0439,18.541 1.474,17.7317 1.1938,16.8079C0.9135,15.8841 0.9378,14.8946 1.2631,13.9856C1.5883,13.0766 2.1973,12.2963 3,11.76V6.5C3,5.837 3.2634,5.2011 3.7322,4.7322C4.2011,4.2634 4.837,4 5.5,4C6.163,4 6.7989,4.2634 7.2678,4.7322C7.7366,5.2011 8,5.837 8,6.5ZM3.049,16H7.949C8.0469,15.5134 7.9983,15.0087 7.8093,14.5498C7.6202,14.0909 7.2992,13.6984 6.887,13.422L6,12.828V6.5C6,6.3674 5.9473,6.2402 5.8535,6.1465C5.7598,6.0527 5.6326,6 5.5,6C5.3674,6 5.2402,6.0527 5.1465,6.1465C5.0527,6.2402 5,6.3674 5,6.5V12.828L4.111,13.422C3.7061,13.6922 3.3887,14.0746 3.1976,14.5223C3.0065,14.97 2.95,15.4637 3.035,15.943C3.0363,15.951 3.0389,15.959 3.0416,15.967C3.0453,15.978 3.049,15.989 3.049,16Z" + android:fillColor="#FF000000" + android:fillType="evenOdd"/> + <path + android:pathData="M20,16V18H14V16H20Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M17,4C15.7113,4.0006 14.457,4.416 13.4228,5.1849C12.3886,5.9538 11.6294,7.0353 11.2577,8.2692C10.8859,9.5031 10.9214,10.824 11.3587,12.0362C11.796,13.2484 12.6121,14.2876 13.686,15H20.314C21.3879,14.2876 22.204,13.2484 22.6413,12.0362C23.0786,10.824 23.1141,9.5031 22.7423,8.2692C22.3706,7.0353 21.6114,5.9538 20.5772,5.1849C19.543,4.416 18.2887,4.0006 17,4Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M18.0607,19.5607C17.7794,19.842 17.3978,20 17,20C16.6022,20 16.2206,19.842 15.9393,19.5607C15.658,19.2794 15.5,18.8978 15.5,18.5H18.5C18.5,18.8978 18.342,19.2794 18.0607,19.5607Z" + android:fillColor="#FF000000" /> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_device_water_off.xml b/packages/SystemUI/res/drawable/ic_device_water_off.xml new file mode 100644 index 000000000000..e1a7846af49a --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_device_water_off.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M17.654,7.563L12,2L6.346,7.563C5.6036,8.2877 5.0136,9.1533 4.6108,10.1094C4.2079,11.0654 4.0002,12.0924 4,13.1299C4,15.2516 4.8429,17.2863 6.3432,18.7866C7.8434,20.2869 9.8783,21.1299 12,21.1299C14.1217,21.1299 16.1566,20.2869 17.6569,18.7866C19.1572,17.2863 20,15.2516 20,13.1299C20,12.0924 19.7925,11.0654 19.3896,10.1094C18.9867,9.1533 18.3966,8.2875 17.654,7.563ZM12,19C10.4265,19.0152 8.9113,18.4056 7.7865,17.3052C6.6617,16.2048 6.0192,14.7033 6,13.1299C5.9996,12.3577 6.1541,11.5933 6.4543,10.8818C6.7546,10.1704 7.1945,9.5262 7.748,8.9878L12,4.8061L16.252,8.9888C16.8056,9.5269 17.2456,10.171 17.5458,10.8823C17.8461,11.5936 18.0005,12.3578 18,13.1299C17.9807,14.7033 17.3383,16.2048 16.2135,17.3052C15.0887,18.4056 13.5735,19.0152 12,19Z" + android:fillColor="#FF000000" /> + <path + android:pathData="M16,12C15.7348,12 15.4804,12.1054 15.2929,12.293C15.1054,12.4805 15,12.7348 15,13C15,13.7956 14.6839,14.5585 14.1213,15.1211C13.5587,15.6837 12.7956,16 12,16C11.7348,16 11.4804,16.1054 11.2929,16.293C11.1054,16.4805 11,16.7348 11,17C11,17.2652 11.1054,17.5195 11.2929,17.707C11.4804,17.8946 11.7348,18 12,18C13.3256,17.9984 14.5964,17.471 15.5338,16.5337C16.4711,15.5964 16.9984,14.3256 17,13C17,12.7348 16.8946,12.4805 16.7071,12.293C16.5196,12.1054 16.2652,12 16,12Z" + android:fillColor="#FF000000" /> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_device_water_on.xml b/packages/SystemUI/res/drawable/ic_device_water_on.xml new file mode 100644 index 000000000000..e57e053b8d4d --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_device_water_on.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M17.654,7.563L12,2L6.346,7.563C5.6036,8.2877 5.0136,9.1533 4.6108,10.1094C4.2079,11.0654 4.0002,12.0924 4,13.1299C4.0174,15.2343 4.87,17.2458 6.3703,18.7217C7.8705,20.1975 9.8956,21.017 12,21C14.1044,21.017 16.1295,20.1975 17.6297,18.7217C19.13,17.2458 19.9826,15.2343 20,13.1299C20,12.0924 19.7925,11.0654 19.3896,10.1094C18.9867,9.1533 18.3966,8.2875 17.654,7.563ZM12,18C11.7348,18 11.4804,17.8946 11.2929,17.707C11.1054,17.5195 11,17.2652 11,17C11,16.7348 11.1054,16.4805 11.2929,16.293C11.4804,16.1054 11.7348,16 12,16C12.7956,16 13.5587,15.6837 14.1213,15.1211C14.6839,14.5585 15,13.7956 15,13C15,12.7348 15.1054,12.4805 15.2929,12.293C15.4804,12.1054 15.7348,12 16,12C16.2652,12 16.5196,12.1054 16.7071,12.293C16.8946,12.4805 17,12.7348 17,13C16.9984,14.3256 16.4711,15.5964 15.5338,16.5337C14.5964,17.471 13.3256,17.9984 12,18Z" + android:fillColor="#FF000000" /> +</vector> diff --git a/packages/SystemUI/res/layout/controls_spinner_item.xml b/packages/SystemUI/res/layout/controls_spinner_item.xml index 00654c832525..45540f102de1 100644 --- a/packages/SystemUI/res/layout/controls_spinner_item.xml +++ b/packages/SystemUI/res/layout/controls_spinner_item.xml @@ -30,6 +30,7 @@ android:layout_gravity="center" android:layout_width="@dimen/controls_header_app_icon_size" android:layout_height="@dimen/controls_header_app_icon_size" + android:contentDescription="@null" android:layout_marginEnd="10dp" /> <TextView diff --git a/packages/SystemUI/res/layout/controls_with_favorites.xml b/packages/SystemUI/res/layout/controls_with_favorites.xml index 91beeb88d87f..b32320956e26 100644 --- a/packages/SystemUI/res/layout/controls_with_favorites.xml +++ b/packages/SystemUI/res/layout/controls_with_favorites.xml @@ -34,6 +34,7 @@ android:orientation="horizontal" android:layout_width="0dp" android:layout_weight="1" + android:minHeight="48dp" android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center"> @@ -43,6 +44,7 @@ android:layout_gravity="center" android:layout_width="@dimen/controls_header_app_icon_size" android:layout_height="@dimen/controls_header_app_icon_size" + android:contentDescription="@null" android:layout_marginEnd="10dp" /> <TextView @@ -72,6 +74,6 @@ android:layout_height="wrap_content" android:orientation="vertical" android:paddingTop="30dp" - android:layout_marginLeft="@dimen/controls_list_side_margin" - android:layout_marginRight="@dimen/controls_list_side_margin" /> + android:layout_marginLeft="@dimen/global_actions_side_margin" + android:layout_marginRight="@dimen/global_actions_side_margin" /> </merge> diff --git a/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml index cb53fe619b24..72cc2ddba282 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml @@ -18,15 +18,16 @@ work around this for now with LinearLayouts. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" + android:layout_width="0dp" + android:layout_weight="1" android:layout_height="wrap_content" android:gravity="center" android:paddingTop="@dimen/global_actions_grid_item_vertical_margin" android:paddingBottom="@dimen/global_actions_grid_item_vertical_margin" android:paddingLeft="@dimen/global_actions_grid_item_side_margin" android:paddingRight="@dimen/global_actions_grid_item_side_margin" - android:layout_marginRight="3dp" - android:layout_marginLeft="3dp" + android:layout_marginRight="@dimen/control_base_item_margin" + android:layout_marginLeft="@dimen/control_base_item_margin" android:background="@drawable/control_background"> <LinearLayout android:layout_width="@dimen/global_actions_grid_item_width" diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml index 620e2e6b509c..59c4d011166a 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml @@ -12,17 +12,17 @@ android:layout_height="wrap_content" android:orientation="horizontal" android:theme="@style/qs_theme" - android:gravity="top | center_horizontal" + android:gravity="top" android:clipChildren="false" android:clipToPadding="false" android:layout_marginTop="@dimen/global_actions_top_margin" > <LinearLayout android:id="@android:id/list" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="@dimen/global_actions_grid_side_margin" - android:layout_marginRight="@dimen/global_actions_grid_side_margin" + android:layout_marginLeft="@dimen/global_actions_side_margin" + android:layout_marginRight="@dimen/global_actions_side_margin" android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" android:paddingRight="@dimen/global_actions_grid_horizontal_padding" android:paddingTop="@dimen/global_actions_grid_vertical_padding" diff --git a/packages/SystemUI/res/values-sw320dp/dimens.xml b/packages/SystemUI/res/values-sw320dp/dimens.xml index 47a2a093302f..c110113e91f4 100644 --- a/packages/SystemUI/res/values-sw320dp/dimens.xml +++ b/packages/SystemUI/res/values-sw320dp/dimens.xml @@ -31,6 +31,6 @@ <dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen> <!-- Home Controls --> - <dimen name="controls_list_side_margin">10dp</dimen> + <dimen name="global_actions_side_margin">10dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw360dp/dimens.xml b/packages/SystemUI/res/values-sw360dp/dimens.xml index 35a653608a90..fc510bf03efd 100644 --- a/packages/SystemUI/res/values-sw360dp/dimens.xml +++ b/packages/SystemUI/res/values-sw360dp/dimens.xml @@ -27,6 +27,6 @@ <dimen name="navigation_side_padding">40dip</dimen> <!-- Home Controls --> - <dimen name="controls_list_side_margin">12dp</dimen> + <dimen name="global_actions_side_margin">12dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw392dp/dimens.xml b/packages/SystemUI/res/values-sw392dp/dimens.xml index 308bc69656da..4c9d02e30bfa 100644 --- a/packages/SystemUI/res/values-sw392dp/dimens.xml +++ b/packages/SystemUI/res/values-sw392dp/dimens.xml @@ -31,6 +31,6 @@ <dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen> <!-- Home Controls --> - <dimen name="controls_list_side_margin">16dp</dimen> + <dimen name="global_actions_side_margin">16dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 288487acec79..4482cdac3327 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -50,6 +50,10 @@ <!-- The color of the text in the Global Actions menu --> <color name="global_actions_alert_text">@color/GM2_red_700</color> + <!-- The color of the background of the emergency button when home controls are visible --> + <color name="global_actions_emergency_background">@color/GM2_red_400</color> + <color name="global_actions_emergency_text">@color/GM2_grey_100</color> + <!-- Tint color for the content on the notification overflow card. --> <color name="keyguard_overflow_content_color">#ff686868</color> @@ -211,6 +215,7 @@ <color name="GM2_red_50">#FCE8E6</color> <color name="GM2_red_200">#F6AEA9</color> <color name="GM2_red_300">#F28B82</color> + <color name="GM2_red_400">#EE675C</color> <color name="GM2_red_500">#B71C1C</color> <color name="GM2_red_700">#C5221F</color> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 344479f371d7..6a8a4b9ac2fd 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1008,6 +1008,9 @@ <dimen name="global_actions_grid_container_shadow_offset">20dp</dimen> <dimen name="global_actions_grid_container_negative_shadow_offset">-20dp</dimen> + <!-- Margins at the left and right of the power menu and home controls widgets. --> + <dimen name="global_actions_side_margin">16dp</dimen> + <!-- The maximum offset in either direction that elements are moved horizontally to prevent burn-in on AOD. --> <dimen name="burn_in_prevention_offset_x">8dp</dimen> @@ -1232,7 +1235,6 @@ <dimen name="controls_header_side_margin">4dp</dimen> <dimen name="controls_header_menu_size">48dp</dimen> <dimen name="controls_header_app_icon_size">40dp</dimen> - <dimen name="controls_list_side_margin">16dp</dimen> <dimen name="controls_top_margin">44dp</dimen> <dimen name="control_header_text_size">22sp</dimen> <dimen name="control_text_size">14sp</dimen> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index aabee1c952e8..3e02b30c376b 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -511,7 +511,7 @@ </style> <style name="TextAppearance.NotificationInfo"> - <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> + <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> <item name="android:textColor">@color/notification_primary_text_color</item> </style> @@ -521,7 +521,6 @@ </style> <style name="TextAppearance.NotificationInfo.Title"> - <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> <item name="android:textColor">@color/notification_primary_text_color</item> <item name="android:textStyle">bold</item> </style> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index 3bda3c8df699..806678f23bb3 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -261,6 +261,11 @@ public class ActivityManagerWrapper { animationHandler.onAnimationCanceled( taskSnapshot != null ? new ThumbnailData(taskSnapshot) : null); } + + @Override + public void onTaskAppeared(RemoteAnimationTarget app) { + animationHandler.onTaskAppeared(new RemoteAnimationTargetCompat(app)); + } }; } ActivityTaskManager.getService().startRecentsActivity(intent, receiver, runner); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index bbb83c73446c..76513c6ff3d5 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -109,4 +109,16 @@ public class RecentsAnimationControllerCompat { Log.e(TAG, "Failed to set overview reached state", e); } } + + /** + * @see IRecentsAnimationController#removeTask + */ + public boolean removeTask(int taskId) { + try { + return mAnimationController.removeTask(taskId); + } catch (RemoteException e) { + Log.e(TAG, "Failed to remove remote animation target", e); + return false; + } + } }
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java index 2c99c5c84015..c4cd192212a0 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java @@ -32,4 +32,10 @@ public interface RecentsAnimationListener { * Called when the animation into Recents was canceled. This call is made on the binder thread. */ void onAnimationCanceled(ThumbnailData thumbnailData); + + /** + * Called when the task of an activity that has been started while the recents animation + * was running becomes ready for control. + */ + void onTaskAppeared(RemoteAnimationTargetCompat app); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 88d6943b4071..367058fa58dd 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -57,7 +57,6 @@ import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; -import android.media.AudioManager; import android.os.CancellationSignal; import android.os.Handler; import android.os.IRemoteCallback; @@ -82,6 +81,8 @@ import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; +import androidx.lifecycle.Observer; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.LockPatternUtils; import com.android.settingslib.WirelessUtils; @@ -97,6 +98,7 @@ import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.util.Assert; +import com.android.systemui.util.RingerModeTracker; import com.google.android.collect.Lists; @@ -258,6 +260,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private TrustManager mTrustManager; private UserManager mUserManager; private KeyguardBypassController mKeyguardBypassController; + private RingerModeTracker mRingerModeTracker; private int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED; private int mFaceRunningState = BIOMETRIC_STATE_STOPPED; private LockPatternUtils mLockPatternUtils; @@ -295,6 +298,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final Handler mHandler; + private final Observer<Integer> mRingerModeObserver = new Observer<Integer>() { + @Override + public void onChanged(Integer ringer) { + mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED, ringer, 0).sendToTarget(); + } + }; + private SparseBooleanArray mFaceSettingEnabledForUser = new SparseBooleanArray(); private BiometricManager mBiometricManager; private IBiometricEnabledOnKeyguardCallback mBiometricEnabledCallback = @@ -1145,9 +1155,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } mHandler.obtainMessage(MSG_SIM_STATE_CHANGE, args.subId, args.slotId, args.simState) .sendToTarget(); - } else if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED, - intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1), 0)); } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) { String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state)); @@ -1501,6 +1508,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mUserTrustIsUsuallyManaged.delete(userId); } + private void registerRingerTracker() { + mRingerModeTracker.getRingerMode().observeForever(mRingerModeObserver); + } + @VisibleForTesting @Inject protected KeyguardUpdateMonitor( @@ -1508,6 +1519,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Main Looper mainLooper, BroadcastDispatcher broadcastDispatcher, DumpManager dumpManager, + RingerModeTracker ringerModeTracker, @Background Executor backgroundExecutor) { mContext = context; mSubscriptionManager = SubscriptionManager.from(context); @@ -1515,6 +1527,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mStrongAuthTracker = new StrongAuthTracker(context, this::notifyStrongAuthStateChanged); mBackgroundExecutor = backgroundExecutor; mBroadcastDispatcher = broadcastDispatcher; + mRingerModeTracker = ringerModeTracker; dumpManager.registerDumpable(getClass().getName(), this); mHandler = new Handler(mainLooper) { @@ -1648,10 +1661,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab filter.addAction(Intent.ACTION_SERVICE_STATE); filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); - filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter, mHandler); + mHandler.post(this::registerRingerTracker); + final IntentFilter allUserFilter = new IntentFilter(); allUserFilter.addAction(Intent.ACTION_USER_INFO_CHANGED); allUserFilter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); @@ -2802,6 +2816,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver); mBroadcastDispatcher.unregisterReceiver(mBroadcastAllReceiver); + mRingerModeTracker.getRingerMode().removeObserver(mRingerModeObserver); mHandler.removeCallbacksAndMessages(null); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 99e5eb66a00f..669a86b8a742 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -599,7 +599,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (mStackView == null) { mStackView = new BubbleStackView( mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator, - mSysUiState); + mSysUiState, mNotificationShadeWindowController); ViewGroup nsv = mNotificationShadeWindowController.getNotificationShadeView(); int bubbleScrimIndex = nsv.indexOfChild(nsv.findViewById(R.id.scrim_for_bubble)); int stackIndex = bubbleScrimIndex + 1; // Show stack above bubble scrim. diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 644e54fb82ae..61e6f39c054f 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -83,6 +83,7 @@ import com.android.systemui.bubbles.animation.StackAnimationController; import com.android.systemui.model.SysUiState; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.SysUiStatsLog; +import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.util.DismissCircleView; import com.android.systemui.util.FloatingContentCoordinator; import com.android.systemui.util.RelativeTouchListener; @@ -330,6 +331,8 @@ public class BubbleStackView extends FrameLayout { @NonNull private final SurfaceSynchronizer mSurfaceSynchronizer; + private final NotificationShadeWindowController mNotificationShadeWindowController; + /** * The currently magnetized object, which is being dragged and will be attracted to the magnetic * dismiss target. @@ -626,13 +629,15 @@ public class BubbleStackView extends FrameLayout { public BubbleStackView(Context context, BubbleData data, @Nullable SurfaceSynchronizer synchronizer, FloatingContentCoordinator floatingContentCoordinator, - SysUiState sysUiState) { + SysUiState sysUiState, + NotificationShadeWindowController notificationShadeWindowController) { super(context); mBubbleData = data; mInflater = LayoutInflater.from(context); mSysUiState = sysUiState; + mNotificationShadeWindowController = notificationShadeWindowController; Resources res = getResources(); mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered); @@ -1557,7 +1562,11 @@ public class BubbleStackView extends FrameLayout { */ @Override public void subtractObscuredTouchableRegion(Region touchableRegion, View view) { - + // If the notification shade is expanded, we shouldn't let the ActivityView steal any touch + // events from any location. + if (mNotificationShadeWindowController.getPanelExpanded()) { + touchableRegion.setEmpty(); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt index 5d03fc51004d..7e8fec716b1f 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt @@ -45,7 +45,7 @@ open class ControlsBindingControllerImpl @Inject constructor( companion object { private const val TAG = "ControlsBindingControllerImpl" private const val MAX_CONTROLS_REQUEST = 100000L - private const val SUGGESTED_CONTROLS_REQUEST = 4L + private const val SUGGESTED_CONTROLS_REQUEST = 6L } private var currentUser = UserHandle.of(ActivityManager.getCurrentUser()) @@ -61,6 +61,11 @@ open class ControlsBindingControllerImpl @Inject constructor( */ private var statefulControlSubscriber: StatefulControlSubscriber? = null + /* + * Will track any active load subscriber. Only one can be active at any time. + */ + private var loadSubscriber: LoadSubscriber? = null + private val actionCallbackService = object : IControlsActionCallback.Stub() { override fun accept( token: IBinder, @@ -99,17 +104,24 @@ open class ControlsBindingControllerImpl @Inject constructor( component: ComponentName, callback: ControlsBindingController.LoadCallback ): Runnable { - val subscriber = LoadSubscriber(callback, MAX_CONTROLS_REQUEST) - retrieveLifecycleManager(component).maybeBindAndLoad(subscriber) - return subscriber.loadCancel() + loadSubscriber?.loadCancel() + + val ls = LoadSubscriber(callback, MAX_CONTROLS_REQUEST) + loadSubscriber = ls + + retrieveLifecycleManager(component).maybeBindAndLoad(ls) + return ls.loadCancel() } override fun bindAndLoadSuggested( component: ComponentName, callback: ControlsBindingController.LoadCallback ) { - val subscriber = LoadSubscriber(callback, SUGGESTED_CONTROLS_REQUEST) - retrieveLifecycleManager(component).maybeBindAndLoadSuggested(subscriber) + loadSubscriber?.loadCancel() + val ls = LoadSubscriber(callback, SUGGESTED_CONTROLS_REQUEST) + loadSubscriber = ls + + retrieveLifecycleManager(component).maybeBindAndLoadSuggested(ls) } override fun subscribe(structureInfo: StructureInfo) { @@ -152,13 +164,16 @@ open class ControlsBindingControllerImpl @Inject constructor( override fun changeUser(newUser: UserHandle) { if (newUser == currentUser) return - unsubscribe() unbind() - currentProvider = null currentUser = newUser } private fun unbind() { + unsubscribe() + + loadSubscriber?.loadCancel() + loadSubscriber = null + currentProvider?.unbindService() currentProvider = null } @@ -210,6 +225,20 @@ open class ControlsBindingControllerImpl @Inject constructor( val callback: ControlsBindingController.LoadCallback ) : CallbackRunnable(token) { override fun doRun() { + Log.d(TAG, "LoadSubscription: Complete and loading controls") + callback.accept(list) + } + } + + private inner class OnCancelAndLoadRunnable( + token: IBinder, + val list: List<Control>, + val subscription: IControlsSubscription, + val callback: ControlsBindingController.LoadCallback + ) : CallbackRunnable(token) { + override fun doRun() { + Log.d(TAG, "LoadSubscription: Canceling and loading controls") + provider?.cancelSubscription(subscription) callback.accept(list) } } @@ -220,6 +249,7 @@ open class ControlsBindingControllerImpl @Inject constructor( val requestLimit: Long ) : CallbackRunnable(token) { override fun doRun() { + Log.d(TAG, "LoadSubscription: Starting subscription") provider?.startSubscription(subscription, requestLimit) } } @@ -254,34 +284,54 @@ open class ControlsBindingControllerImpl @Inject constructor( val requestLimit: Long ) : IControlsSubscriber.Stub() { val loadedControls = ArrayList<Control>() - var hasError = false + private var isTerminated = false private var _loadCancelInternal: (() -> Unit)? = null + private lateinit var subscription: IControlsSubscription + fun loadCancel() = Runnable { - Log.d(TAG, "Cancel load requested") - _loadCancelInternal?.invoke() - } + Log.d(TAG, "Cancel load requested") + _loadCancelInternal?.invoke() + } override fun onSubscribe(token: IBinder, subs: IControlsSubscription) { - _loadCancelInternal = subs::cancel + subscription = subs + _loadCancelInternal = { currentProvider?.cancelSubscription(subscription) } backgroundExecutor.execute(OnSubscribeRunnable(token, subs, requestLimit)) } override fun onNext(token: IBinder, c: Control) { - backgroundExecutor.execute { loadedControls.add(c) } + backgroundExecutor.execute { + if (isTerminated) return@execute + + loadedControls.add(c) + + // Once we have reached our requestLimit, send a request to cancel, and immediately + // load the results. Calls to onError() and onComplete() are not required after + // cancel. + if (loadedControls.size >= requestLimit) { + maybeTerminateAndRun( + OnCancelAndLoadRunnable(token, loadedControls, subscription, callback) + ) + } + } } + override fun onError(token: IBinder, s: String) { - hasError = true - _loadCancelInternal = {} - currentProvider?.cancelLoadTimeout() - backgroundExecutor.execute(OnLoadErrorRunnable(token, s, callback)) + maybeTerminateAndRun(OnLoadErrorRunnable(token, s, callback)) } override fun onComplete(token: IBinder) { + maybeTerminateAndRun(OnLoadRunnable(token, loadedControls, callback)) + } + + private fun maybeTerminateAndRun(postTerminateFn: Runnable) { + if (isTerminated) return + + isTerminated = true _loadCancelInternal = {} - if (!hasError) { - currentProvider?.cancelLoadTimeout() - backgroundExecutor.execute(OnLoadRunnable(token, loadedControls, callback)) - } + currentProvider?.cancelLoadTimeout() + + backgroundExecutor.execute(postTerminateFn) } } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt index ae75dd4d94ab..568fb289027d 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt @@ -180,6 +180,11 @@ interface ControlsController : UserAwareController { fun countFavoritesForComponent(componentName: ComponentName): Int /** + * TEMPORARY for testing + */ + fun resetFavorites() + + /** * Interface for structure to pass data to [ControlsFavoritingActivity]. */ interface LoadData { diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index 34833396acef..8805694616a4 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -365,6 +365,8 @@ class ControlsControllerImpl @Inject constructor ( componentName: ComponentName, callback: Consumer<Boolean> ) { + if (seedingInProgress) return + Log.i(TAG, "Beginning request to seed favorites for: $componentName") if (!confirmAvailability()) { if (userChanging) { @@ -495,6 +497,13 @@ class ControlsControllerImpl @Inject constructor ( } } + override fun resetFavorites() { + executor.execute { + Favorites.clear() + persistenceWrapper.storeFavorites(Favorites.getAllStructures()) + } + } + override fun refreshStatus(componentName: ComponentName, control: Control) { if (!confirmAvailability()) { Log.d(TAG, "Controls not available") diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt index 895f1d218982..a6af6a11d8b7 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt @@ -63,8 +63,6 @@ class ControlsProviderLifecycleManager( ) : IBinder.DeathRecipient { val token: IBinder = Binder() - @GuardedBy("subscriptions") - private val subscriptions = mutableListOf<IControlsSubscription>() private var requiresBound = false @GuardedBy("queuedServiceMethods") private val queuedServiceMethods: MutableSet<ServiceMethod> = ArraySet() @@ -194,7 +192,7 @@ class ControlsProviderLifecycleManager( * Request a call to [IControlsProvider.loadSuggested]. * * If the service is not bound, the call will be queued and the service will be bound first. - * The service will be unbound after the controls are returned or the call times out. + * The service will be unbound if the call times out. * * @param subscriber the subscriber that manages coordination for loading controls */ @@ -245,9 +243,7 @@ class ControlsProviderLifecycleManager( if (DEBUG) { Log.d(TAG, "startSubscription: $subscription") } - synchronized(subscriptions) { - subscriptions.add(subscription) - } + wrapper?.request(subscription, requestLimit) } @@ -261,9 +257,7 @@ class ControlsProviderLifecycleManager( if (DEBUG) { Log.d(TAG, "cancelSubscription: $subscription") } - synchronized(subscriptions) { - subscriptions.remove(subscription) - } + wrapper?.cancel(subscription) } @@ -281,17 +275,6 @@ class ControlsProviderLifecycleManager( onLoadCanceller?.run() onLoadCanceller = null - // be sure to cancel all subscriptions - val subs = synchronized(subscriptions) { - ArrayList(subscriptions).also { - subscriptions.clear() - } - } - - subs.forEach { - wrapper?.cancel(it) - } - bindService(false) } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 208d9117e088..7da3d70271c1 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -16,18 +16,26 @@ package com.android.systemui.controls.ui +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.ObjectAnimator +import android.app.AlertDialog import android.app.Dialog import android.content.ComponentName import android.content.Context +import android.content.DialogInterface import android.content.Intent import android.content.SharedPreferences import android.content.res.Configuration import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable +import android.os.Process import android.service.controls.Control import android.service.controls.actions.ControlAction import android.util.TypedValue import android.util.Log +import android.view.animation.AccelerateInterpolator +import android.view.animation.DecelerateInterpolator import android.view.ContextThemeWrapper import android.view.LayoutInflater import android.view.View @@ -77,6 +85,8 @@ class ControlsUiControllerImpl @Inject constructor ( private const val PREF_COMPONENT = "controls_component" private const val PREF_STRUCTURE = "controls_structure" + private const val FADE_IN_MILLIS = 225L + private val EMPTY_COMPONENT = ComponentName("", "") private val EMPTY_STRUCTURE = StructureInfo( EMPTY_COMPONENT, @@ -153,7 +163,20 @@ class ControlsUiControllerImpl @Inject constructor ( private fun reload(parent: ViewGroup) { if (hidden) return - show(parent) + + val fadeAnim = ObjectAnimator.ofFloat(parent, "alpha", 1.0f, 0.0f) + fadeAnim.setInterpolator(AccelerateInterpolator(1.0f)) + fadeAnim.setDuration(FADE_IN_MILLIS) + fadeAnim.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + show(parent) + val showAnim = ObjectAnimator.ofFloat(parent, "alpha", 0.0f, 1.0f) + showAnim.setInterpolator(DecelerateInterpolator(1.0f)) + showAnim.setDuration(FADE_IN_MILLIS) + showAnim.start() + } + }) + fadeAnim.start() } private fun showSeedingView(items: List<SelectionItem>) { @@ -229,7 +252,8 @@ class ControlsUiControllerImpl @Inject constructor ( private fun createMenu() { val items = arrayOf( - context.resources.getString(R.string.controls_menu_add) + context.resources.getString(R.string.controls_menu_add), + "Reset" ) var adapter = ArrayAdapter<String>(context, R.layout.controls_more_item, items) @@ -249,6 +273,8 @@ class ControlsUiControllerImpl @Inject constructor ( when (pos) { // 0: Add Control 0 -> startFavoritingActivity(view.context, selectedStructure) + // 1: TEMPORARY for reset controls + 1 -> showResetConfirmation() else -> Log.w(ControlsUiController.TAG, "Unsupported index ($pos) on 'more' menu selection") } @@ -275,6 +301,39 @@ class ControlsUiControllerImpl @Inject constructor ( }) } + private fun showResetConfirmation() { + val builder = AlertDialog.Builder( + context, + android.R.style.Theme_DeviceDefault_Dialog_Alert + ).apply { + setMessage("For testing purposes: Would you like to " + + "reset your favorited device controls?") + setPositiveButton( + android.R.string.ok, + DialogInterface.OnClickListener { dialog, _ -> + val userHandle = Process.myUserHandle() + val userContext = context.createContextAsUser(userHandle, 0) + val prefs = userContext.getSharedPreferences( + "controls_prefs", Context.MODE_PRIVATE) + prefs.edit().putBoolean("ControlsSeedingCompleted", false).apply() + controlsController.get().resetFavorites() + dialog.dismiss() + context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) + }) + setNegativeButton( + android.R.string.cancel, + DialogInterface.OnClickListener { + dialog, _ -> dialog.cancel() + } + ) + } + builder.create().apply { + getWindow().apply { + setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY) + } + }.show() + } + private fun createDropDown(items: List<SelectionItem>) { items.forEach { RenderInfo.registerComponentIcon(it.componentName, it.icon) @@ -302,7 +361,6 @@ class ControlsUiControllerImpl @Inject constructor ( .setTint(context.resources.getColor(R.color.control_spinner_dropdown, null)) } parent.requireViewById<ImageView>(R.id.app_icon).apply { - setContentDescription(selectionItem.getTitle()) setImageDrawable(selectionItem.icon) } @@ -370,7 +428,8 @@ class ControlsUiControllerImpl @Inject constructor ( } // add spacers if necessary to keep control size consistent - var spacersToAdd = selectedStructure.controls.size % maxColumns + val mod = selectedStructure.controls.size % maxColumns + var spacersToAdd = if (mod == 0) 0 else maxColumns - mod while (spacersToAdd > 0) { lastRow.addView(Space(context), LinearLayout.LayoutParams(0, 0, 1f)) spacersToAdd-- @@ -526,7 +585,6 @@ private class ItemAdapter( setText(item.getTitle()) } view.requireViewById<ImageView>(R.id.app_icon).apply { - setContentDescription(item.appName) setImageDrawable(item.icon) } return view diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt index 8fd840e6cf58..124df32af5e0 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt @@ -68,7 +68,7 @@ data class RenderInfo( icon = appIconMap.get(componentName) if (icon == null) { icon = context.resources - .getDrawable(R.drawable.ic_device_unknown_gm2_24px, null) + .getDrawable(R.drawable.ic_device_unknown_on, null) appIconMap.put(componentName, icon) } } else { @@ -267,10 +267,74 @@ private val deviceIconMap = mapOf<Int, IconState>( DeviceTypes.TYPE_ROUTINE to IconState( RenderInfo.APP_ICON_ID, RenderInfo.APP_ICON_ID + ), + DeviceTypes.TYPE_AC_HEATER to IconState( + R.drawable.ic_device_thermostat_off, + R.drawable.ic_device_thermostat_on + ), + DeviceTypes.TYPE_AC_UNIT to IconState( + R.drawable.ic_device_thermostat_off, + R.drawable.ic_device_thermostat_on + ), + DeviceTypes.TYPE_COFFEE_MAKER to IconState( + R.drawable.ic_device_kettle_off, + R.drawable.ic_device_kettle_on + ), + DeviceTypes.TYPE_DEHUMIDIFIER to IconState( + R.drawable.ic_device_air_freshener_off, + R.drawable.ic_device_air_freshener_on + ), + DeviceTypes.TYPE_RADIATOR to IconState( + R.drawable.ic_device_thermostat_off, + R.drawable.ic_device_thermostat_on + ), + DeviceTypes.TYPE_STANDMIXER to IconState( + R.drawable.ic_device_cooking_off, + R.drawable.ic_device_cooking_on + ), + DeviceTypes.TYPE_DISPLAY to IconState( + R.drawable.ic_device_display_off, + R.drawable.ic_device_display_on + ), + DeviceTypes.TYPE_DRYER to IconState( + R.drawable.ic_device_washer_off, + R.drawable.ic_device_washer_on + ), + DeviceTypes.TYPE_MOWER to IconState( + R.drawable.ic_device_outdoor_garden_off, + R.drawable.ic_device_outdoor_garden_on + ), + DeviceTypes.TYPE_SHOWER to IconState( + R.drawable.ic_device_water_off, + R.drawable.ic_device_water_on + ), + DeviceTypes.TYPE_AWNING to IconState( + R.drawable.ic_device_pergola_off, + R.drawable.ic_device_pergola_on + ), + DeviceTypes.TYPE_CLOSET to IconState( + R.drawable.ic_device_drawer_off, + R.drawable.ic_device_drawer_on + ), + DeviceTypes.TYPE_CURTAIN to IconState( + R.drawable.ic_device_blinds_off, + R.drawable.ic_device_blinds_on + ), + DeviceTypes.TYPE_DOOR to IconState( + R.drawable.ic_device_door_off, + R.drawable.ic_device_door_on + ), + DeviceTypes.TYPE_SHUTTER to IconState( + R.drawable.ic_device_window_off, + R.drawable.ic_device_window_on + ), + DeviceTypes.TYPE_HEATER to IconState( + R.drawable.ic_device_thermostat_off, + R.drawable.ic_device_thermostat_on ) ).withDefault { IconState( - R.drawable.ic_device_unknown_gm2_24px, - R.drawable.ic_device_unknown_gm2_24px + R.drawable.ic_device_unknown_off, + R.drawable.ic_device_unknown_on ) } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java index 5b3d5c565472..82ccb17a52c6 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java @@ -74,6 +74,8 @@ import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.policy.ZenModeControllerImpl; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerServiceImpl; +import com.android.systemui.util.RingerModeTracker; +import com.android.systemui.util.RingerModeTrackerImpl; import com.android.systemui.volume.VolumeComponent; import com.android.systemui.volume.VolumeDialogComponent; import com.android.systemui.volume.VolumeDialogControllerImpl; @@ -264,4 +266,10 @@ public abstract class DependencyBinder { @Binds public abstract VolumeComponent provideVolumeComponent( VolumeDialogComponent volumeDialogComponent); + + /** + */ + @Binds + public abstract RingerModeTracker provideRingerModeTracker( + RingerModeTrackerImpl ringerModeTrackerImpl); } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 4dd5e87d2c93..a4a589481b33 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -39,9 +39,11 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.UserInfo; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.Color; +import android.graphics.Insets; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.net.ConnectivityManager; @@ -70,6 +72,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; +import android.view.WindowInsets; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.widget.FrameLayout; @@ -77,6 +80,11 @@ import android.widget.ImageView; import android.widget.ImageView.ScaleType; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; + import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; @@ -113,6 +121,7 @@ import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.EmergencyDialerConstants; +import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.leak.RotationUtils; import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator; @@ -129,7 +138,8 @@ import javax.inject.Inject; public class GlobalActionsDialog implements DialogInterface.OnDismissListener, DialogInterface.OnShowListener, ConfigurationController.ConfigurationListener, - GlobalActionsPanelPlugin.Callbacks { + GlobalActionsPanelPlugin.Callbacks, + LifecycleOwner { public static final String SYSTEM_DIALOG_REASON_KEY = "reason"; public static final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions"; @@ -178,6 +188,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final NotificationShadeDepthController mDepthController; private final BlurUtils mBlurUtils; + // Used for RingerModeTracker + private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); + private ArrayList<Action> mItems; private ActionsDialog mDialog; @@ -188,7 +201,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private boolean mKeyguardShowing = false; private boolean mDeviceProvisioned = false; - private ToggleAction.State mAirplaneState = ToggleAction.State.Off; + private ToggleState mAirplaneState = ToggleState.Off; private boolean mIsWaitingForEcmExit = false; private boolean mHasTelephony; private boolean mHasVibrator; @@ -205,7 +218,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final IWindowManager mIWindowManager; private final Executor mBackgroundExecutor; private final ControlsListingController mControlsListingController; - private boolean mAnyControlsProviders = false; + private List<ControlsServiceInfo> mControlsServiceInfos = new ArrayList<>(); + private ControlsController mControlsController; + private SharedPreferences mControlsPreferences; + private final RingerModeTracker mRingerModeTracker; @VisibleForTesting public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum { @@ -244,7 +260,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, ControlsUiController controlsUiController, IWindowManager iWindowManager, @Background Executor backgroundExecutor, ControlsListingController controlsListingController, - ControlsController controlsController, UiEventLogger uiEventLogger) { + ControlsController controlsController, UiEventLogger uiEventLogger, + RingerModeTracker ringerModeTracker) { mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme); mWindowManagerFuncs = windowManagerFuncs; mAudioManager = audioManager; @@ -271,6 +288,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mBackgroundExecutor = backgroundExecutor; mControlsListingController = controlsListingController; mBlurUtils = blurUtils; + mRingerModeTracker = ringerModeTracker; + mControlsController = controlsController; // receive broadcasts IntentFilter filter = new IntentFilter(); @@ -290,6 +309,11 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mShowSilentToggle = SHOW_SILENT_TOGGLE && !resources.getBoolean( R.bool.config_useFixedVolume); + if (mShowSilentToggle) { + mRingerModeTracker.getRingerMode().observe(this, ringer -> + mHandler.sendEmptyMessage(MESSAGE_REFRESH) + ); + } mEmergencyAffordanceManager = new EmergencyAffordanceManager(context); mScreenshotHelper = new ScreenshotHelper(context); @@ -309,45 +333,54 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } }); - String preferredControlsPackage = mContext.getResources() - .getString(com.android.systemui.R.string.config_controlsPreferredPackage); mControlsListingController.addCallback(list -> { - mAnyControlsProviders = !list.isEmpty(); - - /* - * See if any service providers match the preferred component. If they do, - * and there are no current favorites, and we haven't successfully loaded favorites to - * date, query the preferred component for a limited number of suggested controls. - */ - ComponentName preferredComponent = null; - for (ControlsServiceInfo info : list) { - if (info.componentName.getPackageName().equals(preferredControlsPackage)) { - preferredComponent = info.componentName; - break; - } - } - - if (preferredComponent == null) return; - - SharedPreferences prefs = context.getSharedPreferences(PREFS_CONTROLS_FILE, - Context.MODE_PRIVATE); - boolean isSeeded = prefs.getBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, false); - boolean hasFavorites = controlsController.getFavorites().size() > 0; - if (!isSeeded && !hasFavorites) { - controlsController.seedFavoritesForComponent( - preferredComponent, - (accepted) -> { - Log.i(TAG, "Controls seeded: " + accepted); - prefs.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, - accepted).apply(); - } - ); - } + mControlsServiceInfos = list; }); + + // Need to be user-specific with the context to make sure we read the correct prefs + Context userContext = context.createContextAsUser( + new UserHandle(mUserManager.getUserHandle()), 0); + mControlsPreferences = userContext.getSharedPreferences(PREFS_CONTROLS_FILE, + Context.MODE_PRIVATE); + } + private void seedFavorites() { + if (mControlsServiceInfos.isEmpty() + || mControlsController.getFavorites().size() > 0 + || mControlsPreferences.getBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, false)) { + return; + } + /* + * See if any service providers match the preferred component. If they do, + * and there are no current favorites, and we haven't successfully loaded favorites to + * date, query the preferred component for a limited number of suggested controls. + */ + String preferredControlsPackage = mContext.getResources() + .getString(com.android.systemui.R.string.config_controlsPreferredPackage); + ComponentName preferredComponent = null; + for (ControlsServiceInfo info : mControlsServiceInfos) { + if (info.componentName.getPackageName().equals(preferredControlsPackage)) { + preferredComponent = info.componentName; + break; + } + } + + if (preferredComponent == null) { + Log.i(TAG, "Controls seeding: No preferred component has been set, will not seed"); + mControlsPreferences.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, true).apply(); + } + + mControlsController.seedFavoritesForComponent( + preferredComponent, + (accepted) -> { + Log.i(TAG, "Controls seeded: " + accepted); + mControlsPreferences.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, + accepted).apply(); + }); + } /** * Show the global actions dialog (creating if necessary) @@ -393,6 +426,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, awakenIfNecessary(); mDialog = createDialog(); prepareDialog(); + seedFavorites(); // If we only have 1 item and it's a simple press action, just do this action. if (mAdapter.getCount() == 1 @@ -594,7 +628,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, @Override public boolean shouldBeSeparated() { - return true; + return !shouldShowControls(); } @Override @@ -602,7 +636,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, Context context, View convertView, ViewGroup parent, LayoutInflater inflater) { View v = super.create(context, convertView, parent, inflater); int textColor; - if (shouldBeSeparated()) { + if (shouldShowControls()) { + v.setBackgroundTintList(ColorStateList.valueOf(v.getResources().getColor( + com.android.systemui.R.color.global_actions_emergency_background))); + textColor = v.getResources().getColor( + com.android.systemui.R.color.global_actions_emergency_text); + } else if (shouldBeSeparated()) { textColor = v.getResources().getColor( com.android.systemui.R.color.global_actions_alert_text); } else { @@ -612,7 +651,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, TextView messageView = v.findViewById(R.id.message); messageView.setTextColor(textColor); messageView.setSelected(true); // necessary for marquee to work - ImageView icon = (ImageView) v.findViewById(R.id.icon); + ImageView icon = v.findViewById(R.id.icon); icon.getDrawable().setTint(textColor); return v; } @@ -982,18 +1021,15 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, refreshSilentMode(); mAirplaneModeOn.updateState(mAirplaneState); mAdapter.notifyDataSetChanged(); - if (mShowSilentToggle) { - IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION); - mBroadcastDispatcher.registerReceiver(mRingerModeReceiver, filter); - } + mLifecycle.setCurrentState(Lifecycle.State.RESUMED); } private void refreshSilentMode() { if (!mHasVibrator) { - final boolean silentModeOn = - mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL; + Integer value = mRingerModeTracker.getRingerMode().getValue(); + final boolean silentModeOn = value != null && value != AudioManager.RINGER_MODE_NORMAL; ((ToggleAction) mSilentModeAction).updateState( - silentModeOn ? ToggleAction.State.On : ToggleAction.State.Off); + silentModeOn ? ToggleState.On : ToggleState.Off); } } @@ -1005,14 +1041,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mDialog = null; } mWindowManagerFuncs.onGlobalActionsHidden(); - if (mShowSilentToggle) { - try { - mBroadcastDispatcher.unregisterReceiver(mRingerModeReceiver); - } catch (IllegalArgumentException ie) { - // ignore this - Log.w(TAG, ie); - } - } + mLifecycle.setCurrentState(Lifecycle.State.DESTROYED); } /** @@ -1023,6 +1052,13 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mUiEventLogger.log(GlobalActionsEvent.GA_POWER_MENU_OPEN); } + private int getActionLayoutId() { + if (shouldShowControls()) { + return com.android.systemui.R.layout.global_actions_grid_item_v2; + } + return com.android.systemui.R.layout.global_actions_grid_item; + } + /** * The adapter used for the list within the global actions dialog, taking into account whether * the keyguard is showing via @@ -1234,20 +1270,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } } - protected int getActionLayoutId(Context context) { - if (shouldShowControls()) { - return com.android.systemui.R.layout.global_actions_grid_item_v2; - } - return com.android.systemui.R.layout.global_actions_grid_item; - } - public View create( Context context, View convertView, ViewGroup parent, LayoutInflater inflater) { - View v = inflater.inflate(getActionLayoutId(context), parent, - false); + View v = inflater.inflate(getActionLayoutId(), parent, false /* attach */); - ImageView icon = (ImageView) v.findViewById(R.id.icon); - TextView messageView = (TextView) v.findViewById(R.id.message); + ImageView icon = v.findViewById(R.id.icon); + TextView messageView = v.findViewById(R.id.message); messageView.setSelected(true); // necessary for marquee to work if (mIcon != null) { @@ -1266,30 +1294,30 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } } - /** - * A toggle action knows whether it is on or off, and displays an icon and status message - * accordingly. - */ - private static abstract class ToggleAction implements Action { - - enum State { - Off(false), - TurningOn(true), - TurningOff(true), - On(false); + private enum ToggleState { + Off(false), + TurningOn(true), + TurningOff(true), + On(false); - private final boolean inTransition; + private final boolean mInTransition; - State(boolean intermediate) { - inTransition = intermediate; - } + ToggleState(boolean intermediate) { + mInTransition = intermediate; + } - public boolean inTransition() { - return inTransition; - } + public boolean inTransition() { + return mInTransition; } + } - protected State mState = State.Off; + /** + * A toggle action knows whether it is on or off, and displays an icon and status message + * accordingly. + */ + private abstract class ToggleAction implements Action { + + protected ToggleState mState = ToggleState.Off; // prefs protected int mEnabledIconResId; @@ -1333,13 +1361,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, LayoutInflater inflater) { willCreate(); - View v = inflater.inflate(com.android.systemui.R - .layout.global_actions_grid_item, parent, false); + View v = inflater.inflate(getActionLayoutId(), parent, false /* attach */); ImageView icon = (ImageView) v.findViewById(R.id.icon); TextView messageView = (TextView) v.findViewById(R.id.message); final boolean enabled = isEnabled(); - boolean on = ((mState == State.On) || (mState == State.TurningOn)); + boolean on = ((mState == ToggleState.On) || (mState == ToggleState.TurningOn)); if (messageView != null) { messageView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId); @@ -1364,7 +1391,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, return; } - final boolean nowOn = !(mState == State.On); + final boolean nowOn = !(mState == ToggleState.On); onToggle(nowOn); changeStateFromPress(nowOn); } @@ -1381,12 +1408,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, * @param buttonOn Whether the button was turned on or off */ protected void changeStateFromPress(boolean buttonOn) { - mState = buttonOn ? State.On : State.Off; + mState = buttonOn ? ToggleState.On : ToggleState.Off; } abstract void onToggle(boolean on); - public void updateState(State state) { + public void updateState(ToggleState state) { mState = state; } } @@ -1420,7 +1447,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, // In ECM mode airplane state cannot be changed if (!TelephonyProperties.in_ecm_mode().orElse(false)) { - mState = buttonOn ? State.TurningOn : State.TurningOff; + mState = buttonOn ? ToggleState.TurningOn : ToggleState.TurningOff; mAirplaneState = mState; } } @@ -1555,21 +1582,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, public void onServiceStateChanged(ServiceState serviceState) { if (!mHasTelephony) return; final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF; - mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off; + mAirplaneState = inAirplaneMode ? ToggleState.On : ToggleState.Off; mAirplaneModeOn.updateState(mAirplaneState); mAdapter.notifyDataSetChanged(); } }; - private BroadcastReceiver mRingerModeReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) { - mHandler.sendEmptyMessage(MESSAGE_REFRESH); - } - } - }; - private ContentObserver mAirplaneModeObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { @@ -1614,7 +1632,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mContentResolver, Settings.Global.AIRPLANE_MODE_ON, 0) == 1; - mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off; + mAirplaneState = airplaneModeOn ? ToggleState.On : ToggleState.Off; mAirplaneModeOn.updateState(mAirplaneState); } @@ -1631,10 +1649,16 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, intent.putExtra("state", on); mContext.sendBroadcastAsUser(intent, UserHandle.ALL); if (!mHasTelephony) { - mAirplaneState = on ? ToggleAction.State.On : ToggleAction.State.Off; + mAirplaneState = on ? ToggleState.On : ToggleState.Off; } } + @NonNull + @Override + public Lifecycle getLifecycle() { + return mLifecycle; + } + private static final class ActionsDialog extends Dialog implements DialogInterface, ColorExtractor.OnColorsChangedListener { @@ -1888,6 +1912,14 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mGlobalActionsLayout); }) .start(); + ViewGroup root = (ViewGroup) mGlobalActionsLayout.getRootView(); + root.setOnApplyWindowInsetsListener((v, windowInsets) -> { + if (mControlsUiController != null) { + Insets insets = windowInsets.getInsets(WindowInsets.Type.all()); + root.setPadding(insets.left, insets.top, insets.right, insets.bottom); + } + return WindowInsets.CONSUMED; + }); if (mControlsUiController != null) { mControlsUiController.show(mControlsView); } @@ -2017,6 +2049,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private boolean shouldShowControls() { return mKeyguardStateController.isUnlocked() && mControlsUiController.getAvailable() - && mAnyControlsProviders; + && !mControlsServiceInfos.isEmpty(); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java index d219a9e65a3c..dba43430b490 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java @@ -319,6 +319,7 @@ public class PipAnimationController { getSurfaceTransactionHelper() .crop(tx, leash, getDestinationBounds()) .round(tx, leash, shouldApplyCornerRadius()); + tx.show(leash); tx.apply(); } }; @@ -359,6 +360,7 @@ public class PipAnimationController { getSurfaceTransactionHelper() .alpha(tx, leash, 1f) .round(tx, leash, shouldApplyCornerRadius()); + tx.show(leash); tx.apply(); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index bbb493966533..1e9daab4a0b6 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -510,7 +510,7 @@ public class PipTouchHandler { mHandler.removeCallbacks(mShowTargetAction); if (mTargetViewContainer.isAttachedToWindow()) { - mWindowManager.removeView(mTargetViewContainer); + mWindowManager.removeViewImmediate(mTargetViewContainer); } } @@ -592,8 +592,14 @@ public class PipTouchHandler { break; } case MotionEvent.ACTION_HOVER_ENTER: - mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(), - mMovementBounds, false /* allowMenuTimeout */, false /* willResizeMenu */); + // If Touch Exploration is enabled, some a11y services (e.g. Talkback) is probably + // on and changing MotionEvents into HoverEvents. + // Let's not enable menu show/hide for a11y services. + if (!mAccessibilityManager.isTouchExplorationEnabled()) { + mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(), + mMovementBounds, false /* allowMenuTimeout */, + false /* willResizeMenu */); + } case MotionEvent.ACTION_HOVER_MOVE: { if (!shouldDeliverToMenu && !mSendingHoverAccessibilityEvents) { sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER); @@ -602,7 +608,12 @@ public class PipTouchHandler { break; } case MotionEvent.ACTION_HOVER_EXIT: { - mMenuController.hideMenu(); + // If Touch Exploration is enabled, some a11y services (e.g. Talkback) is probably + // on and changing MotionEvents into HoverEvents. + // Let's not enable menu show/hide for a11y services. + if (!mAccessibilityManager.isTouchExplorationEnabled()) { + mMenuController.hideMenu(); + } if (!shouldDeliverToMenu && mSendingHoverAccessibilityEvents) { sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); mSendingHoverAccessibilityEvents = false; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index e1ffad4df2c3..90dc38f3ad12 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -22,10 +22,8 @@ import static com.android.systemui.util.Utils.useQsMediaPlayer; import android.annotation.ColorInt; import android.app.ActivityManager; import android.app.AlarmManager; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; @@ -49,13 +47,16 @@ import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; import com.android.settingslib.Utils; import com.android.systemui.BatteryMeterView; import com.android.systemui.DualToneHandler; import com.android.systemui.R; -import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; @@ -70,6 +71,7 @@ import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.policy.DateView; import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.util.RingerModeTracker; import java.util.ArrayList; import java.util.List; @@ -86,7 +88,7 @@ import javax.inject.Named; */ public class QuickStatusBarHeader extends RelativeLayout implements View.OnClickListener, NextAlarmController.NextAlarmChangeCallback, - ZenModeController.Callback { + ZenModeController.Callback, LifecycleOwner { private static final String TAG = "QuickStatusBarHeader"; private static final boolean DEBUG = false; @@ -137,15 +139,9 @@ public class QuickStatusBarHeader extends RelativeLayout implements private DateView mDateView; private BatteryMeterView mBatteryRemainingIcon; - private BroadcastDispatcher mBroadcastDispatcher; + // Used for RingerModeTracker + private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); - private final BroadcastReceiver mRingerReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - mRingerMode = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1); - updateStatusText(); - } - }; private boolean mHasTopCutout = false; private int mRoundedCornerPadding = 0; @@ -154,7 +150,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements NextAlarmController nextAlarmController, ZenModeController zenModeController, StatusBarIconController statusBarIconController, ActivityStarter activityStarter, - CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher) { + CommandQueue commandQueue, RingerModeTracker ringerModeTracker) { super(context, attrs); mAlarmController = nextAlarmController; mZenController = zenModeController; @@ -162,8 +158,11 @@ public class QuickStatusBarHeader extends RelativeLayout implements mActivityStarter = activityStarter; mDualToneHandler = new DualToneHandler( new ContextThemeWrapper(context, R.style.QSHeaderTheme)); - mBroadcastDispatcher = broadcastDispatcher; mCommandQueue = commandQueue; + ringerModeTracker.getRingerModeInternal().observe(this, ringer -> { + mRingerMode = ringer; + updateStatusText(); + }); } @Override @@ -484,12 +483,11 @@ public class QuickStatusBarHeader extends RelativeLayout implements if (listening) { mZenController.addCallback(this); mAlarmController.addCallback(this); - mBroadcastDispatcher.registerReceiver(mRingerReceiver, - new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)); + mLifecycle.setCurrentState(Lifecycle.State.RESUMED); } else { mZenController.removeCallback(this); mAlarmController.removeCallback(this); - mBroadcastDispatcher.unregisterReceiver(mRingerReceiver); + mLifecycle.setCurrentState(Lifecycle.State.DESTROYED); } } @@ -586,4 +584,10 @@ public class QuickStatusBarHeader extends RelativeLayout implements lp.rightMargin = sideMargins; } } + + @NonNull + @Override + public Lifecycle getLifecycle() { + return mLifecycle; + } } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 366ef931e1f5..2df450604d3b 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -1042,7 +1042,8 @@ public class DividerView extends FrameLayout implements OnTouchListener, dockedTaskRect = dockedTaskRect == null ? dockedRect : dockedTaskRect; otherTaskRect = otherTaskRect == null ? otherRect : otherTaskRect; - mDividerPositionX = dockedRect.right; + mDividerPositionX = mSplitLayout.getPrimarySplitSide() == DOCKED_RIGHT + ? otherRect.right : dockedRect.right; mDividerPositionY = dockedRect.bottom; if (DEBUG) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java index 3b8addb85d85..92f6b4a2d8c4 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java @@ -101,7 +101,16 @@ public class SplitDisplayLayout { } int getPrimarySplitSide() { - return mDisplayLayout.isLandscape() ? DOCKED_LEFT : DOCKED_TOP; + switch (mDisplayLayout.getNavigationBarPosition(mContext.getResources())) { + case DisplayLayout.NAV_BAR_BOTTOM: + return mDisplayLayout.isLandscape() ? DOCKED_LEFT : DOCKED_TOP; + case DisplayLayout.NAV_BAR_LEFT: + return DOCKED_RIGHT; + case DisplayLayout.NAV_BAR_RIGHT: + return DOCKED_LEFT; + default: + return DOCKED_INVALID; + } } boolean isMinimized() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index 5205bab8fea3..7ac066277c86 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -169,7 +169,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { @Override public void onContentUpdated(ExpandableNotificationRow row) { super.onContentUpdated(row); - mIsLowPriority = row.isLowPriority(); + mIsLowPriority = row.getEntry().isAmbient(); mTransformLowPriorityTitle = !row.isChildInGroup() && !row.isSummaryWithChildren(); ArraySet<View> previousViews = mTransformationHelper.getAllTransformingViews(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 14af466a2424..d4f4d3bfbb54 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -39,6 +39,8 @@ import android.telecom.TelecomManager; import android.text.format.DateFormat; import android.util.Log; +import androidx.lifecycle.Observer; + import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.DisplayId; @@ -64,6 +66,7 @@ import com.android.systemui.statusbar.policy.RotationLockController.RotationLock import com.android.systemui.statusbar.policy.SensorPrivacyController; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.time.DateFormatUtil; import java.util.Locale; @@ -109,7 +112,6 @@ public class PhoneStatusBarPolicy private final SharedPreferences mSharedPreferences; private final DateFormatUtil mDateFormatUtil; private final TelecomManager mTelecomManager; - private final AudioManager mAudioManager; private final Handler mHandler = new Handler(); private final CastController mCast; @@ -132,6 +134,7 @@ public class PhoneStatusBarPolicy private final Executor mUiBgExecutor; private final SensorPrivacyController mSensorPrivacyController; private final RecordingController mRecordingController; + private final RingerModeTracker mRingerModeTracker; private boolean mZenVisible; private boolean mVolumeVisible; @@ -154,10 +157,11 @@ public class PhoneStatusBarPolicy KeyguardStateController keyguardStateController, LocationController locationController, SensorPrivacyController sensorPrivacyController, IActivityManager iActivityManager, - AlarmManager alarmManager, UserManager userManager, AudioManager audioManager, + AlarmManager alarmManager, UserManager userManager, RecordingController recordingController, @Nullable TelecomManager telecomManager, @DisplayId int displayId, - @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil) { + @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil, + RingerModeTracker ringerModeTracker) { mIconController = iconController; mCommandQueue = commandQueue; mBroadcastDispatcher = broadcastDispatcher; @@ -179,8 +183,8 @@ public class PhoneStatusBarPolicy mSensorPrivacyController = sensorPrivacyController; mRecordingController = recordingController; mUiBgExecutor = uiBgExecutor; - mAudioManager = audioManager; mTelecomManager = telecomManager; + mRingerModeTracker = ringerModeTracker; mSlotCast = resources.getString(com.android.internal.R.string.status_bar_cast); mSlotHotspot = resources.getString(com.android.internal.R.string.status_bar_hotspot); @@ -208,8 +212,7 @@ public class PhoneStatusBarPolicy public void init() { // listen for broadcasts IntentFilter filter = new IntentFilter(); - filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); - filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION); + filter.addAction(AudioManager.ACTION_HEADSET_PLUG); filter.addAction(Intent.ACTION_SIM_STATE_CHANGED); filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED); @@ -217,6 +220,10 @@ public class PhoneStatusBarPolicy filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler); + Observer<Integer> observer = ringer -> mHandler.post(this::updateVolumeZen); + + mRingerModeTracker.getRingerMode().observeForever(observer); + mRingerModeTracker.getRingerModeInternal().observeForever(observer); // listen for user / profile change. try { @@ -350,14 +357,18 @@ public class PhoneStatusBarPolicy } if (!ZenModeConfig.isZenOverridingRinger(zen, mZenController.getConsolidatedPolicy())) { - if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) { - volumeVisible = true; - volumeIconId = R.drawable.stat_sys_ringer_vibrate; - volumeDescription = mResources.getString(R.string.accessibility_ringer_vibrate); - } else if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) { - volumeVisible = true; - volumeIconId = R.drawable.stat_sys_ringer_silent; - volumeDescription = mResources.getString(R.string.accessibility_ringer_silent); + final Integer ringerModeInternal = + mRingerModeTracker.getRingerModeInternal().getValue(); + if (ringerModeInternal != null) { + if (ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) { + volumeVisible = true; + volumeIconId = R.drawable.stat_sys_ringer_vibrate; + volumeDescription = mResources.getString(R.string.accessibility_ringer_vibrate); + } else if (ringerModeInternal == AudioManager.RINGER_MODE_SILENT) { + volumeVisible = true; + volumeIconId = R.drawable.stat_sys_ringer_silent; + volumeDescription = mResources.getString(R.string.accessibility_ringer_silent); + } } } @@ -616,10 +627,6 @@ public class PhoneStatusBarPolicy public void onReceive(Context context, Intent intent) { String action = intent.getAction(); switch (action) { - case AudioManager.RINGER_MODE_CHANGED_ACTION: - case AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION: - updateVolumeZen(); - break; case Intent.ACTION_SIM_STATE_CHANGED: // Avoid rebroadcast because SysUI is direct boot aware. if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) { diff --git a/packages/SystemUI/src/com/android/systemui/util/RingerModeTracker.kt b/packages/SystemUI/src/com/android/systemui/util/RingerModeTracker.kt new file mode 100644 index 000000000000..047298e6ed81 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/RingerModeTracker.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util + +import androidx.lifecycle.LiveData + +interface RingerModeTracker { + + val ringerMode: LiveData<Int> + val ringerModeInternal: LiveData<Int> +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/util/RingerModeTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/util/RingerModeTrackerImpl.kt new file mode 100644 index 000000000000..58684c386995 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/RingerModeTrackerImpl.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.media.AudioManager +import android.os.UserHandle +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.dagger.qualifiers.Background +import java.util.concurrent.Executor +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class RingerModeTrackerImpl @Inject constructor( + audioManager: AudioManager, + broadcastDispatcher: BroadcastDispatcher, + @Background executor: Executor +) : RingerModeTracker { + + override val ringerMode: LiveData<Int> = RingerModeLiveData( + broadcastDispatcher, + executor, + AudioManager.RINGER_MODE_CHANGED_ACTION, + audioManager::getRingerMode + ) + + override val ringerModeInternal: LiveData<Int> = RingerModeLiveData( + broadcastDispatcher, + executor, + AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION, + audioManager::getRingerModeInternal + ) +} + +class RingerModeLiveData( + private val broadcastDispatcher: BroadcastDispatcher, + private val executor: Executor, + intent: String, + private val getter: () -> Int +) : MutableLiveData<Int>() { + + private val filter = IntentFilter(intent) + var initialSticky: Boolean = false + private set + + private val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + initialSticky = isInitialStickyBroadcast + postValue(intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1)) + } + } + + override fun getValue(): Int { + return super.getValue() ?: -1 + } + + override fun onActive() { + super.onActive() + broadcastDispatcher.registerReceiver(receiver, filter, executor, UserHandle.ALL) + executor.execute { + postValue(getter()) + } + } + + override fun onInactive() { + super.onInactive() + broadcastDispatcher.unregisterReceiver(receiver) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index 73532632c875..f19c49cc123f 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -55,6 +55,8 @@ import android.util.Log; import android.util.Slog; import android.view.accessibility.AccessibilityManager; +import androidx.lifecycle.Observer; + import com.android.internal.annotations.GuardedBy; import com.android.settingslib.volume.MediaSessions; import com.android.systemui.Dumpable; @@ -64,6 +66,8 @@ import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.util.RingerModeLiveData; +import com.android.systemui.util.RingerModeTracker; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -122,6 +126,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private final NotificationManager mNoMan; private final SettingObserver mObserver; private final Receiver mReceiver = new Receiver(); + private final RingerModeObservers mRingerModeObservers; private final MediaSessions mMediaSessions; protected C mCallbacks = new C(); private final State mState = new State(); @@ -145,7 +150,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa @Inject public VolumeDialogControllerImpl(Context context, BroadcastDispatcher broadcastDispatcher, - Optional<Lazy<StatusBar>> statusBarOptionalLazy) { + Optional<Lazy<StatusBar>> statusBarOptionalLazy, RingerModeTracker ringerModeTracker) { mContext = context.getApplicationContext(); // TODO(b/150663459): remove this TV workaround once StatusBar is "unbound" on TVs if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { @@ -164,6 +169,11 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mAudio = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); mNoMan = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); mObserver = new SettingObserver(mWorker); + mRingerModeObservers = new RingerModeObservers( + (RingerModeLiveData) ringerModeTracker.getRingerMode(), + (RingerModeLiveData) ringerModeTracker.getRingerModeInternal() + ); + mRingerModeObservers.init(); mBroadcastDispatcher = broadcastDispatcher; mObserver.init(); mReceiver.init(); @@ -246,6 +256,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mMediaSessions.destroy(); mObserver.destroy(); mReceiver.destroy(); + mRingerModeObservers.destroy(); mWorkerThread.quitSafely(); } @@ -528,7 +539,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa ss.name = STREAMS.get(stream); checkRoutedToBluetoothW(stream); } - updateRingerModeExternalW(mAudio.getRingerMode()); + // We are not destroyed so this is listening and has updated information + updateRingerModeExternalW(mRingerModeObservers.mRingerMode.getValue()); updateZenModeW(); updateZenConfig(); updateEffectsSuppressorW(mNoMan.getEffectsSuppressor()); @@ -575,7 +587,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa Events.writeEvent(Events.EVENT_MUTE_CHANGED, stream, muted); } if (muted && isRinger(stream)) { - updateRingerModeInternalW(mAudio.getRingerModeInternal()); + updateRingerModeInternalW(mRingerModeObservers.mRingerModeInternal.getValue()); } return true; } @@ -964,6 +976,79 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } } + private final class RingerModeObservers { + + private final RingerModeLiveData mRingerMode; + private final RingerModeLiveData mRingerModeInternal; + + private final Observer<Integer> mRingerModeObserver = new Observer<Integer>() { + @Override + public void onChanged(Integer value) { + mWorker.post(() -> { + final int rm = value; + if (mRingerMode.getInitialSticky()) { + mState.ringerModeExternal = rm; + } + if (D.BUG) { + Log.d(TAG, "onChange ringer_mode rm=" + + Util.ringerModeToString(rm)); + } + if (updateRingerModeExternalW(rm)) { + mCallbacks.onStateChanged(mState); + } + } + ); + } + }; + + private final Observer<Integer> mRingerModeInternalObserver = new Observer<Integer>() { + @Override + public void onChanged(Integer value) { + mWorker.post(() -> { + final int rm = value; + if (mRingerModeInternal.getInitialSticky()) { + mState.ringerModeInternal = rm; + } + if (D.BUG) { + Log.d(TAG, "onChange internal_ringer_mode rm=" + + Util.ringerModeToString(rm)); + } + if (updateRingerModeInternalW(rm)) { + mCallbacks.onStateChanged(mState); + } + } + ); + } + }; + + RingerModeObservers(RingerModeLiveData ringerMode, + RingerModeLiveData ringerModeInternal) { + mRingerMode = ringerMode; + mRingerModeInternal = ringerModeInternal; + } + + public void init() { + int initialValue = mRingerMode.getValue(); + if (initialValue != -1) { + // If it's not -1, set it to the initial value, if it's -1, it means that the + // tracker is not listening already and will obtain the sticky value. + mState.ringerModeExternal = initialValue; + } + mRingerMode.observeForever(mRingerModeObserver); + initialValue = mRingerModeInternal.getValue(); + if (initialValue != -1) { + // If it's not -1, set it to the initial value, if it's -1, it means that the + // tracker is not listening already and will obtain the sticky value. + mState.ringerModeInternal = initialValue; + } + mRingerModeInternal.observeForever(mRingerModeInternalObserver); + } + + public void destroy() { + mRingerMode.removeObserver(mRingerModeObserver); + mRingerModeInternal.removeObserver(mRingerModeInternalObserver); + } + } private final class SettingObserver extends ContentObserver { private final Uri ZEN_MODE_URI = @@ -1006,8 +1091,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa final IntentFilter filter = new IntentFilter(); filter.addAction(AudioManager.VOLUME_CHANGED_ACTION); filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION); - filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); - filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION); filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION); filter.addAction(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); @@ -1042,18 +1125,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa + stream + " devices=" + devices + " oldDevices=" + oldDevices); changed = checkRoutedToBluetoothW(stream); changed |= onVolumeChangedW(stream, 0); - } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) { - final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1); - if (isInitialStickyBroadcast()) mState.ringerModeExternal = rm; - if (D.BUG) Log.d(TAG, "onReceive RINGER_MODE_CHANGED_ACTION rm=" - + Util.ringerModeToString(rm)); - changed = updateRingerModeExternalW(rm); - } else if (action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) { - final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1); - if (isInitialStickyBroadcast()) mState.ringerModeInternal = rm; - if (D.BUG) Log.d(TAG, "onReceive INTERNAL_RINGER_MODE_CHANGED_ACTION rm=" - + Util.ringerModeToString(rm)); - changed = updateRingerModeInternalW(rm); } else if (action.equals(AudioManager.STREAM_MUTE_CHANGED_ACTION)) { final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); final boolean muted = intent diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java index 4652abfa0721..cfec1c07ff1d 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java +++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java @@ -27,6 +27,7 @@ import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; +import android.annotation.IntDef; import android.annotation.NonNull; import android.content.ContentResolver; import android.content.Context; @@ -46,16 +47,27 @@ import android.view.Surface; import com.android.internal.R; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Contains information about the layout-properties of a display. This refers to internal layout * like insets/cutout/rotation. In general, this can be thought of as the System-UI analog to * DisplayPolicy. */ public class DisplayLayout { + @IntDef(prefix = { "NAV_BAR_" }, value = { + NAV_BAR_LEFT, + NAV_BAR_RIGHT, + NAV_BAR_BOTTOM, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NavBarPosition {} + // Navigation bar position values - private static final int NAV_BAR_LEFT = 1 << 0; - private static final int NAV_BAR_RIGHT = 1 << 1; - private static final int NAV_BAR_BOTTOM = 1 << 2; + public static final int NAV_BAR_LEFT = 1 << 0; + public static final int NAV_BAR_RIGHT = 1 << 1; + public static final int NAV_BAR_BOTTOM = 1 << 2; private int mUiMode; private int mWidth; @@ -214,6 +226,14 @@ public class DisplayLayout { } /** + * Gets navigation bar position for this layout + * @return Navigation bar position for this layout. + */ + public @NavBarPosition int getNavigationBarPosition(Resources res) { + return navigationBarPosition(res, mWidth, mHeight, mRotation); + } + + /** * Rotates bounds as if parentBounds and bounds are a group. The group is rotated by `delta` * 90-degree counter-clockwise increments. This assumes that parentBounds is at 0,0 and * remains at 0,0 after rotation. @@ -437,8 +457,8 @@ public class DisplayLayout { } /** Retrieve navigation bar position from resources based on rotation and size. */ - public static int navigationBarPosition(Resources res, int displayWidth, int displayHeight, - int rotation) { + public static @NavBarPosition int navigationBarPosition(Resources res, int displayWidth, + int displayHeight, int rotation) { boolean navBarCanMove = displayWidth != displayHeight && res.getBoolean( com.android.internal.R.bool.config_navBarCanMove); if (navBarCanMove && displayWidth > displayHeight) { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java index d47fceea9724..64590fd3a36b 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java @@ -25,6 +25,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Test; import org.junit.runner.RunWith; @@ -36,6 +37,7 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { @UiThreadTest @Test public void showSecurityScreen_canInflateAllModes() { + mDependency.injectMockDependency(KeyguardStateController.class); KeyguardSecurityContainer keyguardSecurityContainer = new KeyguardSecurityContainer(getContext()); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 73f9d8ad6953..7403a11fecbf 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -29,6 +29,8 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -50,6 +52,7 @@ import android.hardware.biometrics.BiometricSourceType; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; +import android.media.AudioManager; import android.os.Bundle; import android.os.Handler; import android.os.IRemoteCallback; @@ -63,18 +66,24 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableContext; import android.testing.TestableLooper; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.Observer; + import com.android.internal.telephony.TelephonyIntents; import com.android.keyguard.KeyguardUpdateMonitor.BiometricAuthenticated; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.util.RingerModeTracker; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -125,6 +134,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { private BroadcastDispatcher mBroadcastDispatcher; @Mock private Executor mBackgroundExecutor; + @Mock + private RingerModeTracker mRingerModeTracker; + @Mock + private LiveData<Integer> mRingerModeLiveData; private TestableLooper mTestableLooper; private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor; @@ -156,6 +169,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { context.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager); context.addMockSystemService(SubscriptionManager.class, mSubscriptionManager); + when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData); + mTestableLooper = TestableLooper.get(this); allowTestableLooperAsMainThread(); mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(context); @@ -613,6 +628,29 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { assertThat(mKeyguardUpdateMonitor.getSecondaryLockscreenRequirement(user)).isNull(); } + @Test + public void testRingerModeChange() { + ArgumentCaptor<Observer<Integer>> captor = ArgumentCaptor.forClass(Observer.class); + verify(mRingerModeLiveData).observeForever(captor.capture()); + Observer<Integer> observer = captor.getValue(); + + KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class); + + mKeyguardUpdateMonitor.registerCallback(callback); + + observer.onChanged(AudioManager.RINGER_MODE_NORMAL); + observer.onChanged(AudioManager.RINGER_MODE_SILENT); + observer.onChanged(AudioManager.RINGER_MODE_VIBRATE); + + mTestableLooper.processAllMessages(); + + InOrder orderVerify = inOrder(callback); + orderVerify.verify(callback).onRingerModeChanged(anyInt()); // Initial update on register + orderVerify.verify(callback).onRingerModeChanged(AudioManager.RINGER_MODE_NORMAL); + orderVerify.verify(callback).onRingerModeChanged(AudioManager.RINGER_MODE_SILENT); + orderVerify.verify(callback).onRingerModeChanged(AudioManager.RINGER_MODE_VIBRATE); + } + private void setBroadcastReceiverPendingResult(BroadcastReceiver receiver) { BroadcastReceiver.PendingResult pendingResult = new BroadcastReceiver.PendingResult(Activity.RESULT_OK, @@ -643,7 +681,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { protected TestableKeyguardUpdateMonitor(Context context) { super(context, TestableLooper.get(KeyguardUpdateMonitorTest.this).getLooper(), - mBroadcastDispatcher, mDumpManager, mBackgroundExecutor); + mBroadcastDispatcher, mDumpManager, + mRingerModeTracker, mBackgroundExecutor); mStrongAuthTracker = KeyguardUpdateMonitorTest.this.mStrongAuthTracker; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java index 32604f8609fa..b2c35867e789 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java @@ -38,6 +38,8 @@ import com.android.systemui.broadcast.FakeBroadcastDispatcher; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.statusbar.SmartReplyController; +import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import org.junit.After; import org.junit.Before; @@ -97,6 +99,12 @@ public abstract class SysuiTestCase { // A lot of tests get the LocalBluetoothManager, often via several layers of indirection. // None of them actually need it. mDependency.injectMockDependency(LocalBluetoothManager.class); + + // Notifications tests are injecting one of these, causing many classes (including + // KeyguardUpdateMonitor to be created (injected). + // TODO(b/1531701009) Clean up NotificationContentView creation to prevent this + mDependency.injectMockDependency(SmartReplyController.class); + mDependency.injectMockDependency(NotificationBlockingHelperManager.class); } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt index 88316f2d4323..bb003ee59978 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt @@ -125,7 +125,7 @@ class ControlsBindingControllerImplTest : SysuiTestCase() { loadSubscriberCaptor.value.onSubscribe(Binder(), subscription) canceller.run() - verify(subscription).cancel() + verify(providers[0]).cancelSubscription(subscription) } @Test @@ -145,7 +145,7 @@ class ControlsBindingControllerImplTest : SysuiTestCase() { loadSubscriberCaptor.value.onComplete(b) canceller.run() - verify(subscription, never()).cancel() + verify(providers[0], never()).cancelSubscription(subscription) } @Test @@ -203,7 +203,7 @@ class ControlsBindingControllerImplTest : SysuiTestCase() { loadSubscriberCaptor.value.onError(b, "") canceller.run() - verify(subscription, never()).cancel() + verify(providers[0], never()).cancelSubscription(subscription) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java index 137a126f539d..300cb212ffb1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java @@ -18,6 +18,7 @@ package com.android.systemui.globalactions; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.IActivityManager; import android.app.admin.DevicePolicyManager; @@ -52,6 +53,8 @@ import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.RingerModeLiveData; +import com.android.systemui.util.RingerModeTracker; import org.junit.Before; import org.junit.Test; @@ -95,6 +98,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase { @Mock private ControlsListingController mControlsListingController; @Mock private ControlsController mControlsController; @Mock private UiEventLogger mUiEventLogger; + @Mock private RingerModeTracker mRingerModeTracker; + @Mock private RingerModeLiveData mRingerModeLiveData; private TestableLooper mTestableLooper; @@ -103,6 +108,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mTestableLooper = TestableLooper.get(this); allowTestableLooperAsMainThread(); + + when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData); mGlobalActionsDialog = new GlobalActionsDialog(mContext, mWindowManagerFuncs, mAudioManager, @@ -133,7 +140,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase { mBackgroundExecutor, mControlsListingController, mControlsController, - mUiEventLogger + mUiEventLogger, + mRingerModeTracker ); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java index 0d66340a3917..56a748497d4e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java @@ -51,7 +51,6 @@ public class PipAnimationControllerTest extends SysuiTestCase { private PipAnimationController mPipAnimationController; - @Mock private SurfaceControl mLeash; @Mock @@ -61,6 +60,10 @@ public class PipAnimationControllerTest extends SysuiTestCase { public void setUp() throws Exception { mPipAnimationController = new PipAnimationController( mContext, new PipSurfaceTransactionHelper(mContext)); + mLeash = new SurfaceControl.Builder() + .setContainerLayer() + .setName("FakeLeash") + .build(); MockitoAnnotations.initMocks(this); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index be026f7884c3..1c6e5a36bd8a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.notification.row; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; +import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP; + import static junit.framework.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; @@ -139,6 +141,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { private NotificationRowBinderImpl mRowBinder; private Handler mHandler; private FakeExecutor mBgExecutor; + private RowContentBindStage mRowContentBindStage; @Before public void setUp() { @@ -147,10 +150,13 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { mHandler = Handler.createAsync(TestableLooper.get(this).getLooper()); + // Add an action so heads up content views are made + Notification.Action action = new Notification.Action.Builder(null, null, null).build(); Notification notification = new Notification.Builder(mContext) .setSmallIcon(R.drawable.ic_person) .setContentTitle(TEST_TITLE) .setContentText(TEST_TEXT) + .setActions(action) .build(); mSbn = new SbnBuilder() .setNotification(notification) @@ -192,11 +198,11 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { () -> mock(SmartReplyController.class), mock(ConversationNotificationProcessor.class), mBgExecutor); - RowContentBindStage stage = new RowContentBindStage( + mRowContentBindStage = new RowContentBindStage( binder, mock(NotifInflationErrorManager.class), mock(RowContentBindStageLogger.class)); - pipeline.setStage(stage); + pipeline.setStage(mRowContentBindStage); ArgumentCaptor<ExpandableNotificationRow> viewCaptor = ArgumentCaptor.forClass(ExpandableNotificationRow.class); @@ -232,7 +238,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { "FOOBAR", "FOOBAR", mKeyguardBypassController, mGroupManager, - stage, + mRowContentBindStage, mock(NotificationLogger.class), mHeadsUpManager, mPresenter, @@ -256,7 +262,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { mRemoteInputManager, mLockscreenUserManager, pipeline, - stage, + mRowContentBindStage, mNotificationInterruptionStateProvider, RowInflaterTask::new, mExpandableNotificationRowComponentBuilder, @@ -365,6 +371,27 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { verify(mPresenter).updateNotificationViews(); } + @Test + public void testContentViewInflationDuringRowInflationInflatesCorrectViews() { + // GIVEN a notification is added and the row is inflating + mEntryManager.addNotification(mSbn, mRankingMap); + ArgumentCaptor<NotificationEntry> entryCaptor = ArgumentCaptor.forClass( + NotificationEntry.class); + verify(mEntryListener).onPendingEntryAdded(entryCaptor.capture()); + NotificationEntry entry = entryCaptor.getValue(); + + // WHEN we try to bind a content view + mRowContentBindStage.getStageParams(entry).requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP); + mRowContentBindStage.requestRebind(entry, null); + + waitForInflation(); + + // THEN the notification has its row and all relevant content views inflated + assertNotNull(entry.getRow()); + assertNotNull(entry.getRow().getPrivateLayout().getContractedChild()); + assertNotNull(entry.getRow().getPrivateLayout().getHeadsUpChild()); + } + /** * Wait for inflation to finish. * diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt new file mode 100644 index 000000000000..2489c301f6f5 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util + +import android.content.BroadcastReceiver +import android.content.IntentFilter +import android.os.UserHandle +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.lifecycle.Observer +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.broadcast.BroadcastDispatcher +import org.junit.After +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.MockitoAnnotations +import java.util.concurrent.Executor + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class RingerModeLiveDataTest : SysuiTestCase() { + + companion object { + private fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() + private fun <T> any(): T = Mockito.any() + private fun <T> eq(value: T): T = Mockito.eq(value) ?: value + private val INTENT = "INTENT" + } + + @Mock + private lateinit var broadcastDispatcher: BroadcastDispatcher + @Mock + private lateinit var valueSupplier: () -> Int + @Mock + private lateinit var observer: Observer<Int> + @Captor + private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver> + @Captor + private lateinit var intentFilterCaptor: ArgumentCaptor<IntentFilter> + + // Run everything immediately + private val executor = Executor { it.run() } + private lateinit var liveData: RingerModeLiveData + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + liveData = RingerModeLiveData(broadcastDispatcher, executor, INTENT, valueSupplier) + } + + @After + fun tearDown() { + liveData.removeObserver(observer) + } + + @Test + fun testInit_broadcastNotRegistered() { + verifyNoMoreInteractions(broadcastDispatcher) + } + + @Test + fun testOnActive_broadcastRegistered() { + liveData.observeForever(observer) + verify(broadcastDispatcher).registerReceiver(any(), any(), eq(executor), eq(UserHandle.ALL)) + } + + @Test + fun testOnActive_intentFilterHasIntent() { + liveData.observeForever(observer) + verify(broadcastDispatcher).registerReceiver(any(), capture(intentFilterCaptor), any(), + any()) + assertTrue(intentFilterCaptor.value.hasAction(INTENT)) + } + + @Test + fun testOnActive_valueObtained() { + liveData.observeForever(observer) + verify(valueSupplier).invoke() + } + + @Test + fun testOnInactive_broadcastUnregistered() { + liveData.observeForever(observer) + liveData.removeObserver(observer) + verify(broadcastDispatcher).unregisterReceiver(any()) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java index 8cc83dd99b53..6166cd76e005 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java @@ -31,6 +31,7 @@ import android.media.session.MediaSession; import android.os.Handler; import android.os.Process; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import androidx.test.filters.SmallTest; @@ -38,7 +39,10 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.util.RingerModeLiveData; +import com.android.systemui.util.RingerModeTracker; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,6 +53,7 @@ import java.util.Optional; @RunWith(AndroidTestingRunner.class) @SmallTest +@TestableLooper.RunWithLooper public class VolumeDialogControllerImplTest extends SysuiTestCase { TestableVolumeDialogControllerImpl mVolumeController; @@ -56,18 +61,35 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { StatusBar mStatusBar; @Mock private BroadcastDispatcher mBroadcastDispatcher; + @Mock + private RingerModeTracker mRingerModeTracker; + @Mock + private RingerModeLiveData mRingerModeLiveData; + @Mock + private RingerModeLiveData mRingerModeInternalLiveData; @Before public void setup() throws Exception { MockitoAnnotations.initMocks(this); + when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData); + when(mRingerModeTracker.getRingerModeInternal()).thenReturn(mRingerModeInternalLiveData); + // Initial non-set value + when(mRingerModeLiveData.getValue()).thenReturn(-1); + when(mRingerModeInternalLiveData.getValue()).thenReturn(-1); + mCallback = mock(VolumeDialogControllerImpl.C.class); mStatusBar = mock(StatusBar.class); mVolumeController = new TestableVolumeDialogControllerImpl(mContext, mCallback, mStatusBar, - mBroadcastDispatcher); + mBroadcastDispatcher, mRingerModeTracker); mVolumeController.setEnableDialogs(true, true); } + @After + public void tearDown() { + mVolumeController.destroy(); + } + @Test public void testRegisteredWithDispatcher() { verify(mBroadcastDispatcher).registerReceiverWithHandler(any(BroadcastReceiver.class), @@ -109,7 +131,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { TestableVolumeDialogControllerImpl nullStatusBarTestableDialog = new TestableVolumeDialogControllerImpl( - mContext, callback, null, mBroadcastDispatcher); + mContext, callback, null, mBroadcastDispatcher, mRingerModeTracker); nullStatusBarTestableDialog.setEnableDialogs(true, true); nullStatusBarTestableDialog.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI); verify(callback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED); @@ -127,12 +149,26 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { mVolumeController.mMediaSessionsCallbacksW.onRemoteRemoved(token); } + @Test + public void testRingerModeLiveDataObserving() { + verify(mRingerModeLiveData).observeForever(any()); + verify(mRingerModeInternalLiveData).observeForever(any()); + } + + @Test + public void testRingerModeOnDestroy_observersRemoved() { + mVolumeController.destroy(); + + verify(mRingerModeLiveData).removeObserver(any()); + verify(mRingerModeInternalLiveData).removeObserver(any()); + } + static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl { TestableVolumeDialogControllerImpl(Context context, C callback, StatusBar s, - BroadcastDispatcher broadcastDispatcher) { + BroadcastDispatcher broadcastDispatcher, RingerModeTracker ringerModeTracker) { super( context, broadcastDispatcher, - s == null ? Optional.empty() : Optional.of(() -> s)); + s == null ? Optional.empty() : Optional.of(() -> s), ringerModeTracker); mCallbacks = callback; } } diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 8c1360cca940..ca1b27bd261e 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -3669,6 +3669,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku // Default launcher from package manager. final ComponentName defaultLauncher = mPackageManagerInternal .getDefaultHomeActivity(UserHandle.getUserId(callingUid)); + if (defaultLauncher == null) { + return; + } int defaultLauncherUid = 0; try { defaultLauncherUid = mPackageManager.getApplicationInfo( diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java index 8b50b010a22c..b6bc7c5646a5 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java @@ -58,6 +58,7 @@ import com.android.internal.os.IResultReceiver; import com.android.server.autofill.ui.InlineSuggestionFactory; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.TimeUnit; @@ -263,6 +264,8 @@ final class RemoteAugmentedAutofillService && fieldIds.get(0).equals(focusedId); client.autofill(sessionId, fieldIds, dataset.getFieldValues(), hideHighlight); + inlineSuggestionsCallback.apply(new InlineSuggestionsResponse( + Collections.EMPTY_LIST)); } catch (RemoteException e) { Slog.w(TAG, "Encounter exception autofilling the values"); } diff --git a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java index 7ad5632dd70e..347174c4c804 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java @@ -27,7 +27,9 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteCallback; import android.service.autofill.IInlineSuggestionRenderService; import android.service.autofill.IInlineSuggestionUiCallback; import android.service.autofill.InlinePresentation; @@ -91,6 +93,15 @@ public final class RemoteInlineSuggestionRenderService extends hostInputToken, displayId)); } + /** + * Gets the inline suggestions renderer info as a {@link Bundle}. + */ + public void getInlineSuggestionsRendererInfo(@NonNull RemoteCallback callback) { + scheduleAsyncRequest((s) -> s.getInlineSuggestionsRendererInfo(new RemoteCallback( + (bundle) -> callback.sendResult(bundle) + ))); + } + @Nullable private static ServiceInfo getServiceInfo(Context context, int userId) { final String packageName = diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 4ecffd8d29b0..3d6861898aaf 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -652,10 +652,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return mService.isInlineSuggestionsEnabled(); } - private boolean isInlineSuggestionRenderServiceAvailable() { - return mService.getRemoteInlineSuggestionRenderServiceLocked() != null; - } - /** * Clears the existing response for the partition, reads a new structure, and then requests a * new fill response from the fill service. @@ -715,14 +711,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Only ask IME to create inline suggestions request if Autofill provider supports it and // the render service is available. - if (isInlineSuggestionsEnabledByAutofillProviderLocked() - && isInlineSuggestionRenderServiceAvailable()) { + final RemoteInlineSuggestionRenderService remoteRenderService = + mService.getRemoteInlineSuggestionRenderServiceLocked(); + if (isInlineSuggestionsEnabledByAutofillProviderLocked() && remoteRenderService != null) { Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer = mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ true); if (inlineSuggestionsRequestConsumer != null) { - // TODO(b/146454892): pipe the uiExtras from the ExtServices. - mInlineSessionController.onCreateInlineSuggestionsRequestLocked(mCurrentViewId, - inlineSuggestionsRequestConsumer, Bundle.EMPTY); + remoteRenderService.getInlineSuggestionsRendererInfo( + new RemoteCallback((extras) -> { + mInlineSessionController.onCreateInlineSuggestionsRequestLocked( + mCurrentViewId, inlineSuggestionsRequestConsumer, extras); + } + )); } } else { mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ false); @@ -1228,6 +1228,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } catch (RemoteException e) { Slog.e(TAG, "Error requesting to hide fill UI", e); } + + mInlineSessionController.hideInlineSuggestionsUiLocked(id); } } @@ -3128,13 +3130,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // 1. the field is augmented autofill only (when standard autofill provider is None or // when it returns null response) // 2. standard autofill provider doesn't support inline suggestion - if (isInlineSuggestionRenderServiceAvailable() + final RemoteInlineSuggestionRenderService remoteRenderService = + mService.getRemoteInlineSuggestionRenderServiceLocked(); + if (remoteRenderService != null && (mForAugmentedAutofillOnly || !isInlineSuggestionsEnabledByAutofillProviderLocked())) { if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill"); - // TODO(b/146454892): pipe the uiExtras from the ExtServices. - mInlineSessionController.onCreateInlineSuggestionsRequestLocked(mCurrentViewId, - /*requestConsumer=*/ requestAugmentedAutofill, Bundle.EMPTY); + remoteRenderService.getInlineSuggestionsRendererInfo(new RemoteCallback( + (extras) -> { + mInlineSessionController.onCreateInlineSuggestionsRequestLocked( + mCurrentViewId, /*requestConsumer=*/ requestAugmentedAutofill, + extras); + }, mHandler)); } else { requestAugmentedAutofill.accept( mInlineSessionController.getInlineSuggestionsRequestLocked().orElse(null)); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 9aefc8dc78ec..b0a586d122ea 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -220,6 +220,10 @@ class StorageManagerService extends IStorageManager.Stub private static final boolean ENABLE_ISOLATED_STORAGE = StorageManager.hasIsolatedStorage(); + // A system property to control if obb app data isolation is enabled in vold. + private static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY = + "persist.sys.vold_app_data_isolation_enabled"; + /** * If {@code 1}, enables the isolated storage feature. If {@code -1}, * disables the isolated storage feature. If {@code 0}, uses the default @@ -596,6 +600,8 @@ class StorageManagerService extends IStorageManager.Stub private final boolean mIsFuseEnabled; + private final boolean mVoldAppDataIsolationEnabled; + @GuardedBy("mLock") private final Set<Integer> mUidsWithLegacyExternalStorage = new ArraySet<>(); // Not guarded by lock, always used on the ActivityManager thread @@ -1516,7 +1522,7 @@ class StorageManagerService extends IStorageManager.Stub if (vol.type == VolumeInfo.TYPE_EMULATED) { if (newState != VolumeInfo.STATE_MOUNTED) { mFuseMountedUser.remove(vol.getMountUserId()); - } else { + } else if (mVoldAppDataIsolationEnabled){ final int userId = vol.getMountUserId(); mFuseMountedUser.add(userId); // Async remount app storage so it won't block the main thread. @@ -1740,6 +1746,8 @@ class StorageManagerService extends IStorageManager.Stub // incorrect until #updateFusePropFromSettings where we set the correct value and reboot if // different mIsFuseEnabled = SystemProperties.getBoolean(PROP_FUSE, DEFAULT_FUSE_ENABLED); + mVoldAppDataIsolationEnabled = mIsFuseEnabled && SystemProperties.getBoolean( + ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false); mContext = context; mResolver = mContext.getContentResolver(); mCallbacks = new Callbacks(FgThread.get().getLooper()); diff --git a/services/core/java/com/android/server/am/OomAdjuster.md b/services/core/java/com/android/server/am/OomAdjuster.md new file mode 100644 index 000000000000..eda511ab7369 --- /dev/null +++ b/services/core/java/com/android/server/am/OomAdjuster.md @@ -0,0 +1,129 @@ +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +# Oom Adjuster Designs + +## Purpose of Oom Adjuster + +The Android OS runs with limited hardware resources, i.e. CPU/RAM/Power. To strive for the better performance, Oom Ajuster is introduced to tweak the following 3 major factors: + + * Process State + * Wildly used by the System Server, i.e., determine if it's foreground or not, change the GC behavior, etc. + * Defined in `ActivityManager#PROCESS_STATE_*` + * Oom Adj score + * Used by the lmkd to determine which process should be expunged on memory pressure. + * Defined in `ProcessList#*_ADJ` + * Scheduler Group + * Used to tweak the process group, thread priorities. + * Top process is scheduled to be running on a dedicated big core, while foreground processes take the other big cores; background processes stay with LITTLE cores instead. + +## Process Capabilities + +Besides the above 3 major factors, Android R introduced the Process Capabilities `ActivityManager#PROCESS_CAPABILITY_*`. It's a new attribute to process record, mainly designed for supporting the "while-in-use" permission model - in additional to the traditional Android permissions, wheather or not a process has access to a given API, will be guarded by its current process state as well. The OomAdjuster will compute the process capabilities during updating the oom adj. Meanwhile, the flag `ActivityManager#BIND_INCLUDE_CAPABILITIES` enables to possiblity to "transfer" the capability from a client process to the service process it binds to. + +## Rationale of Oom Adjuster + +System server keeps a list of recent used app processes. Given the 4 types of entities that an Android processes could have: Activity, Service, Content Provider and Broadcast Receiver, the System Server has to adjust the above 3 factors to give the users the best performance according to the states of the entities. A typical case would be that: foreground app A binds into a background service B in order to serve the user, in the case of memory pressure, the background service B should be avoided from being expunged since it would result user-perceptible interruption of service. The Oom Adjuster is to tweak the aforementioned 3 factors for those app processes. + +The timing of updating the Oom Adj score is vital: assume a camera process in background gets launched into foreground, launching camera typically incurs high memory pressure, which could incur low memory kills - if the camera process isn't moved out of the background adj group, it could get killed by lmkd. Therefore the updates have to be called pretty frequently: in case there is an activity start, service binding, etc. + +The update procedure basically consists of 3 parts: + * Find out the process record to be updated + * There are two categories of updateOomAdjLocked: one with the target process record to be updated, while the other one is to update all process record. + * Besides that, while computing the Oom Aj score, the clients of service connections or content providers of the present process record, which forms a process dependency graph actually, will be evaluated as well. + * Starting from Android R, when updating for a specific process record, an optimization is made that, only the reachable process records starting from this process record in the process dependency graph, will be re-evaluated. + * The `cached` Oom Adj scores are grouped in `bucket`, which is used in the isolated processes: they could be correlated - assume one isolated Chrome process is at Oom Adj score 920 and another one is 980; the later one could get expunged much earlier than the former one, which doesn't make sense; grouping them would be a big relief for this case. + * Compute Oom Adj score + * This procedure returns true if there is a score change, false if there is no. + * The curAdj field in the process record is used as an intermediate value during the computation. + * Initialize the Process State to `PROCESS_STATE_CACHED_EMPTY`, which is the lowest importance. + * Calculate the scores based on various factors: + * If it's not allowed to be lower than `ProcessList#FOREGROUND_APP_ADJ`, meaning it's propbably a persistent process, there is no too much to do here. + * Exame if the process is the top app, running remote animation, running instrumentation, receiving broadcast, executing services, running on top but sleeping (screen off), update the intermediate values. + * Ask Window Manager (yes, ActivityTaskManager is with WindowManager now) to tell each activity's visibility information. + * Check if the process has recent tasks, check if it's hosting a foreground service, overlay UI, toast etc. Note for the foreground service, if it was in foreground status, allow it to stay in higher rank in memory for a while: Assuming a camera captureing case, where the camera app is still processing the picture while being switched out of foreground - keep it stay in higher rank in memory would ensure the pictures are persisted correctly. + * Check if the process is the heavy weight process, whose launching/exiting would be slow and it's better to keep it in the memory. Note there should be only one heavy weight process across the system. + * For sure the Home process shouldn't be expunged frequently as well. + * The next two factors are either it was the previous process with visible UI to the user, or it's a backup agent. + * And then it goes to the massive searches against the service connections and the content providers, each of the clients will be evaluated, and the Oom Adj score could get updated according to its clients' scores. However there are a bunch of service binding flags which could impact the result: + * Below table captures the results with given various service binding states: + | Conditon #1 | Condition #2 | Condition #3 | Condition #4 | Result | + |---------------------------------|------------------------------------------------------------|----------------------------------------------|---------------------------------------------------|--------------------------| + | `BIND_WAIVE_PRIORITY` not set | `BIND_ALLOW_OOM_MANAGEMENT` set | Shown UI && Not Home | | Use the app's own Adj | + | | | Inactive for a while | | Use the app's own Adj | + | | Client has a higher importance | Shown UI && Not Home && client is invisible | | Use the app's own Adj | + | | | `BIND_ABOVE_CLIENT` and `BIND_IMPORTANT` set | Client is not persistent | Try client's Adj | + | | | | Client is persistent | Try persistent Adj | + | | | `BIND_NOT_PERCEPTIBLE` set | client < perceptible && app > low perceptible | Try low perceptible Adj | + | | | `BIND_NOT_VISIBLE` set | client < perceptible && app > perceptible | Try perceptible Adj | + | | | Client >= perceptible | | Try client's Adj | + | | | Adj > visible | | Max of client/Own Adj | + | | | | | Use the app's own Adj | + | | `BIND_NOT_FOREGROUND`+`BIND_IMPORTANT_BACKGROUND` not set | Client's sched group > app's | `BIND_IMPORTANT` is set | Use client's sched group | + | | | | | Use default sched group | + | | | Client's process state < top | `BIND_FOREGROUND_SERVICE` is set | ProcState = bound fg | + | | | | `BIND_FOREGROUND_SERVICE_WHILE_AWAKE` + screen ON | ProcState = bound fg | + | | | | | ProcState = important fg | + | | | Client's process state = top | | ProcState = bound top | + | | `BIND_IMPORTANT_BACKGROUND` not set | Client's process state < transient bg | | ProcState = transient bg | + | | `BIND_NOT_FOREGROUND` or `BIND_IMPORTANT_BACKGROUND` set | Client's process state < important bg | | ProcState = important bg | + | `BIND_ADJUST_WITH_ACTIVITY` set | Adj > fg && App visible | | | Adj = foreground | + | | | `BIND_NOT_FOREGROUND` not set | `BIND_IMPORTANT` is set | Sched = top app bound | + | | | | `BIND_IMPORTANT` is NOT set | Sched = default | + * Below table captures the results with given various content provider binding states: + | Conditon #1 | Condition #2 | Condition #3 | Result | + |---------------------------------|------------------------------------------------------------|----------------------------------------------|--------------------------| + | Client's process state >= cached| | | Client ProcState = empty | + | Adj > Client Adj | Not shown UI or is Home, or Client's Adj <= perceptible | Client's Adj <= foreground Adj | Try foreground Adj | + | | | Client's Adj > foreground Adj | Try client's Adj | + | Client's process state <= fg svc| Client's process state is top | | ProcState = bound top | + | | Client's process state is NOT top | | ProcState = bound fg svc | + | Has external dependencies | Adj > fg app | | adj = fg app | + | | Process state > important foreground | | ProcState = important fg | + | Still within retain time | Adj > previous app Adj | | adj = previuos app adj | + | | Process state > last activity | | ProcState = last activity| + * Some additional tweaks after the above ones: + | Conditon #1 | Condition #2 | Condition #3 | Result | + |---------------------------------|------------------------------------------------------------|----------------------------------------------|------------------------------------| + | Process state >= cached empty | Has client activities | | ProcState = cached activity client | + | | treat like activity (IME) | | ProcState = cached activity | + | Adj is service adj | computing all process records | Num of new service A > 1/3 of services | Push it to service B | + | | | Low on RAM and app process's PSS is large | Push it to service B | + * Apply the scores, which consists of: write into kernel sysfs entries to update the Oom Adj scores; call kernel API to set the thread priorities, and then tell the world the new process state + +## Cycles, Cycles, Cycles + +Another interesting aspect of the Oom Adjuster is the cycles of the dependencies. A simple example would be like below illustration, process A is hosting a service which is bound by process B; meanwhile the process B is hosting a service which is bound by process A. +<pre> + +-------------+ +-------------+ + | Process A | <-------- | Process B | + | (service 1) | --------> | (service 2) | + +-------------+ +-------------+ +</pre> + +There could be very complicated cases, which could involve multiple cycles, and in the dependency graph, each of the process record node could have different importance. +<pre> + +-------------+ +-------------+ +-------------+ +-------------+ +-------------+ + | Process D | --------> | Process A | <-------- | Process B | <-------- | Process C | <-------- | Process A | + | | | (service 1) | | (service 2) | | (service 3) | | (service 1) | + +-------------+ +-------------+ +-------------+ +-------------+ +-------------+ +</pre> + +The Oom Adjuster maintains a global sequence ID `mAdjSeq` to track the current Oom Adjuster calling. And each of the process record has a field to track in which sequence the process record is evaluated. If during the Oom Adj computation, a process record with sequence ID as same as the current global sequence ID, this would mean that a cycle is detected; in this case: + * Decrement the sequence ID of each process if there is a cycle. + * Re-evaluate each of the process record within the cycle until nothing was promoted. + * Iterate the processes from least important to most important ones. + * A maximum retries of 10 is enforced, while in practice, the maximum retries could reach only 2 to 3. + diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 786e9cf66bfa..89fa02bbbd64 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -154,6 +154,9 @@ public final class ProcessList { static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY = "persist.sys.vold_app_data_isolation_enabled"; + // A system property to control if fuse is enabled. + static final String ANDROID_FUSE_ENABLED = "persist.sys.fuse"; + // The minimum time we allow between crashes, for us to consider this // application to be bad and stop and its services and reject broadcasts. static final int MIN_CRASH_INTERVAL = 60 * 1000; @@ -707,8 +710,13 @@ public final class ProcessList { // want some apps enabled while some apps disabled mAppDataIsolationEnabled = SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true); - mVoldAppDataIsolationEnabled = SystemProperties.getBoolean( + boolean fuseEnabled = SystemProperties.getBoolean(ANDROID_FUSE_ENABLED, false); + boolean voldAppDataIsolationEnabled = SystemProperties.getBoolean( ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false); + if (!fuseEnabled && voldAppDataIsolationEnabled) { + Slog.e(TAG, "Fuse is not enabled while vold app data isolation is enabled"); + } + mVoldAppDataIsolationEnabled = fuseEnabled && voldAppDataIsolationEnabled; mAppDataIsolationWhitelistedApps = new ArrayList<>( SystemConfig.getInstance().getAppDataIsolationWhitelistedApps()); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 30e765f3d602..02d8571c2dcd 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -3622,7 +3622,15 @@ public class AudioService extends IAudioService.Stub hdlr = h; // Remove from client list so that it is re-inserted at top of list iter.remove(); - hdlr.getBinder().unlinkToDeath(hdlr, 0); + try { + hdlr.getBinder().unlinkToDeath(hdlr, 0); + if (cb != hdlr.getBinder()) { + hdlr = null; + } + } catch (NoSuchElementException e) { + hdlr = null; + Log.w(TAG, "link does not exist ..."); + } break; } } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index e7d0c41c0fea..65b7cf3eabd1 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -51,6 +51,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.WorkSource; +import android.os.storage.StorageManager; import android.util.Log; import android.util.Slog; import android.util.SparseArray; @@ -79,7 +80,7 @@ import java.util.Map; * Helper class for running dexopt command on packages. */ public class PackageDexOptimizer { - private static final String TAG = "PackageManager.DexOptimizer"; + private static final String TAG = "PackageDexOptimizer"; static final String OAT_DIR_NAME = "oat"; // TODO b/19550105 Remove error codes and use exceptions public static final int DEX_OPT_SKIPPED = 0; @@ -307,6 +308,55 @@ public class PackageDexOptimizer { } } + /** + * Perform dexopt (if needed) on a system server code path). + */ + public int dexoptSystemServerPath( + String dexPath, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) { + int dexoptFlags = DEXOPT_PUBLIC + | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0) + | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0); + + int result = DEX_OPT_SKIPPED; + for (String isa : dexUseInfo.getLoaderIsas()) { + int dexoptNeeded = getDexoptNeeded( + dexPath, + isa, + options.getCompilerFilter(), + dexUseInfo.getClassLoaderContext(), + /* newProfile= */false, + /* downgrade= */ false); + + if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) { + continue; + } + try { + mInstaller.dexopt( + dexPath, + android.os.Process.SYSTEM_UID, + /* packageName= */ "android", + isa, + dexoptNeeded, + /* oatDir= */ null, + dexoptFlags, + options.getCompilerFilter(), + StorageManager.UUID_PRIVATE_INTERNAL, + dexUseInfo.getClassLoaderContext(), + /* seInfo= */ null, + /* downgrade= */ false , + /* targetSdk= */ 0, + /* profileName */ null, + /* dexMetadataPath */ null, + getReasonName(options.getCompilationReason())); + } catch (InstallerException e) { + Slog.w(TAG, "Failed to dexopt", e); + return DEX_OPT_FAILED; + } + result = DEX_OPT_PERFORMED; + } + return result; + } + private String getAugmentedReasonName(int compilationReason, boolean useDexMetadata) { String annotation = useDexMetadata ? ArtManagerService.DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION : ""; diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index f07fa501d3a8..2221644bff47 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -128,6 +128,7 @@ import com.android.internal.content.PackageHelper; import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.pm.Installer.InstallerException; @@ -1801,6 +1802,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + private void logDataLoaderInstallationSession(int returnCode, String extraMessage) { + final long currentTimestamp = System.currentTimeMillis(); + FrameworkStatsLog.write(FrameworkStatsLog.PACKAGE_INSTALLER_V2_REPORTED, + isIncrementalInstallation(), + mPackageName, + currentTimestamp - createdMillis, + returnCode); + } + /** * Returns true if the session should attempt to inherit any existing native libraries already * extracted at the current install location. This is necessary to prevent double loading of @@ -2789,6 +2799,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } mCallback.onSessionFinished(this, success); + if (isDataLoaderInstallation()) { + logDataLoaderInstallationSession(returnCode, msg); + } } /** {@hide} */ diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index f693555d9761..9c41e6d5c6a3 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -9855,6 +9855,11 @@ public class PackageManagerService extends IPackageManager.Stub private int performDexOptInternalWithDependenciesLI(AndroidPackage p, @NonNull PackageSetting pkgSetting, DexoptOptions options) { + // System server gets a special path. + if (PLATFORM_PACKAGE_NAME.equals(p.getPackageName())) { + return mDexManager.dexoptSystemServer(options); + } + // Select the dex optimizer based on the force parameter. // Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to // allocate an object here. diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index ebdf85691e58..eb7057dd2994 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -49,6 +49,8 @@ import dalvik.system.VMRuntime; import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -437,15 +439,7 @@ public class DexManager { * because they don't need to be compiled).. */ public boolean dexoptSecondaryDex(DexoptOptions options) { - // Select the dex optimizer based on the force parameter. - // Forced compilation is done through ForcedUpdatePackageDexOptimizer which will adjust - // the necessary dexopt flags to make sure that compilation is not skipped. This avoid - // passing the force flag through the multitude of layers. - // Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to - // allocate an object here. - PackageDexOptimizer pdo = options.isForce() - ? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer) - : mPackageDexOptimizer; + PackageDexOptimizer pdo = getPackageDexOptimizer(options); String packageName = options.getPackageName(); PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName); if (useInfo.getDexUseInfoMap().isEmpty()) { @@ -486,6 +480,83 @@ public class DexManager { } /** + * Performs dexopt on system server dex files. + * + * <p>Verfifies that the package name is {@link PackageManagerService#PLATFORM_PACKAGE_NAME}. + * + * @return + * <p>PackageDexOptimizer.DEX_OPT_SKIPPED if dexopt was skipped because no system server + * files were recorded or if no dexopt was needed. + * <p>PackageDexOptimizer.DEX_OPT_FAILED if any dexopt operation failed. + * <p>PackageDexOptimizer.DEX_OPT_PERFORMED if all dexopt operations succeeded. + */ + public int dexoptSystemServer(DexoptOptions options) { + if (!PLATFORM_PACKAGE_NAME.equals(options.getPackageName())) { + Slog.wtf(TAG, "Non system server package used when trying to dexopt system server:" + + options.getPackageName()); + return PackageDexOptimizer.DEX_OPT_FAILED; + } + + PackageDexOptimizer pdo = getPackageDexOptimizer(options); + String packageName = options.getPackageName(); + PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName); + if (useInfo.getDexUseInfoMap().isEmpty()) { + if (DEBUG) { + Slog.d(TAG, "No dex files recorded for system server"); + } + // Nothing to compile, return true. + return PackageDexOptimizer.DEX_OPT_SKIPPED; + } + + boolean usageUpdated = false; + int result = PackageDexOptimizer.DEX_OPT_SKIPPED; + for (Map.Entry<String, DexUseInfo> entry : useInfo.getDexUseInfoMap().entrySet()) { + String dexPath = entry.getKey(); + DexUseInfo dexUseInfo = entry.getValue(); + if (!Files.exists(Paths.get(dexPath))) { + if (DEBUG) { + Slog.w(TAG, "A dex file previously loaded by System Server does not exist " + + " anymore: " + dexPath); + } + usageUpdated = mPackageDexUsage.removeDexFile( + packageName, dexPath, dexUseInfo.getOwnerUserId()) || usageUpdated; + continue; + } + + int newResult = pdo.dexoptSystemServerPath(dexPath, dexUseInfo, options); + + // The end result is: + // - FAILED if any path failed, + // - PERFORMED if at least one path needed compilation, + // - SKIPPED when all paths are up to date + if ((result != PackageDexOptimizer.DEX_OPT_FAILED) + && (newResult != PackageDexOptimizer.DEX_OPT_SKIPPED)) { + result = newResult; + } + } + + if (usageUpdated) { + mPackageDexUsage.maybeWriteAsync(); + } + + return result; + } + + /** + * Select the dex optimizer based on the force parameter. + * Forced compilation is done through ForcedUpdatePackageDexOptimizer which will adjust + * the necessary dexopt flags to make sure that compilation is not skipped. This avoid + * passing the force flag through the multitude of layers. + * Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to + * allocate an object here. + */ + private PackageDexOptimizer getPackageDexOptimizer(DexoptOptions options) { + return options.isForce() + ? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer) + : mPackageDexOptimizer; + } + + /** * Reconcile the information we have about the secondary dex files belonging to * {@code packagName} and the actual dex files. For all dex files that were * deleted, update the internal records and delete any generated oat files. diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index cd6c5bfaeb79..5a3464d8a35f 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -3219,6 +3219,10 @@ public final class PowerManagerService extends SystemService private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm, @Nullable final String reason, boolean wait) { if (PowerManager.REBOOT_USERSPACE.equals(reason)) { + if (!PowerManager.isRebootingUserspaceSupportedImpl()) { + throw new UnsupportedOperationException( + "Attempted userspace reboot on a device that doesn't support it"); + } UserspaceRebootLogger.noteUserspaceRebootWasRequested(); } if (mHandler == null || !mSystemReady) { diff --git a/services/core/java/com/android/server/textclassifier/IconsContentProvider.java b/services/core/java/com/android/server/textclassifier/IconsContentProvider.java new file mode 100644 index 000000000000..d19a707770e2 --- /dev/null +++ b/services/core/java/com/android/server/textclassifier/IconsContentProvider.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.textclassifier; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.net.Uri; +import android.os.ParcelFileDescriptor; +import android.os.ParcelFileDescriptor.AutoCloseOutputStream; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.textclassifier.IconsUriHelper.ResourceInfo; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * A content provider that is used to access icons returned from the TextClassifier service. + * + * <p>Use {@link IconsUriHelper#getContentUri(String, int)} to access a uri for a specific resource. + * The uri may be passed to other processes to access the specified resource. + * + * <p>NOTE: Care must be taken to avoid leaking resources to non-permitted apps via this provider. + */ +public final class IconsContentProvider extends ContentProvider { + + private static final String TAG = "IconsContentProvider"; + + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) { + try { + final ResourceInfo res = IconsUriHelper.getInstance().getResourceInfo(uri); + final Drawable drawable = Icon.createWithResource(res.packageName, res.id) + .loadDrawable(getContext()); + final byte[] data = getBitmapData(drawable); + final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); + final ParcelFileDescriptor readSide = pipe[0]; + final ParcelFileDescriptor writeSide = pipe[1]; + try (OutputStream out = new AutoCloseOutputStream(writeSide)) { + out.write(data); + return readSide; + } + } catch (IOException | RuntimeException e) { + Log.e(TAG, "Error retrieving icon for uri: " + uri, e); + } + return null; + } + + /** + * Returns the bitmap data for the specified drawable. + */ + @VisibleForTesting + public static byte[] getBitmapData(Drawable drawable) { + if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { + throw new IllegalStateException("The icon is zero-sized"); + } + + final Bitmap bitmap = Bitmap.createBitmap( + drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), + Bitmap.Config.ARGB_8888); + + final Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + + final ByteArrayOutputStream stream = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); + final byte[] byteArray = stream.toByteArray(); + bitmap.recycle(); + return byteArray; + } + + @Override + public String getType(Uri uri) { + return "image/png"; + } + + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } +} diff --git a/services/core/java/com/android/server/textclassifier/IconsUriHelper.java b/services/core/java/com/android/server/textclassifier/IconsUriHelper.java new file mode 100644 index 000000000000..f17b0f14bd0e --- /dev/null +++ b/services/core/java/com/android/server/textclassifier/IconsUriHelper.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.textclassifier; + +import android.annotation.Nullable; +import android.net.Uri; +import android.util.ArrayMap; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.function.Supplier; + +/** + * A helper for mapping an icon resource to a content uri. + * + * <p>NOTE: Care must be taken to avoid passing resource uris to non-permitted apps via this helper. + */ +@VisibleForTesting(visibility = Visibility.PACKAGE) +public final class IconsUriHelper { + + public static final String AUTHORITY = "com.android.textclassifier.icons"; + + private static final String TAG = "IconsUriHelper"; + private static final Supplier<String> DEFAULT_ID_SUPPLIER = () -> UUID.randomUUID().toString(); + + // TODO: Consider using an LRU cache to limit resource usage. + // This may depend on the expected number of packages that a device typically has. + @GuardedBy("mPackageIds") + private final Map<String, String> mPackageIds = new ArrayMap<>(); + + private final Supplier<String> mIdSupplier; + + private static final IconsUriHelper sSingleton = new IconsUriHelper(null); + + private IconsUriHelper(@Nullable Supplier<String> idSupplier) { + mIdSupplier = (idSupplier != null) ? idSupplier : DEFAULT_ID_SUPPLIER; + + // Useful for testing: + // Magic id for the android package so it is the same across classloaders. + // This is okay as this package does not have access restrictions, and + // the TextClassifierService hardly returns icons from this package. + mPackageIds.put("android", "android"); + } + + /** + * Returns a new instance of this object for testing purposes. + */ + public static IconsUriHelper newInstanceForTesting(@Nullable Supplier<String> idSupplier) { + return new IconsUriHelper(idSupplier); + } + + static IconsUriHelper getInstance() { + return sSingleton; + } + + /** + * Returns a Uri for the specified icon resource. + * + * @param packageName the resource's package name + * @param resId the resource id + * @see #getResourceInfo(Uri) + */ + public Uri getContentUri(String packageName, int resId) { + Objects.requireNonNull(packageName); + synchronized (mPackageIds) { + if (!mPackageIds.containsKey(packageName)) { + // TODO: Ignore packages that don't actually exist on the device. + mPackageIds.put(packageName, mIdSupplier.get()); + } + return new Uri.Builder() + .scheme("content") + .authority(AUTHORITY) + .path(mPackageIds.get(packageName)) + .appendPath(Integer.toString(resId)) + .build(); + } + } + + /** + * Returns a valid {@link ResourceInfo} for the specified uri. Returns {@code null} if a valid + * {@link ResourceInfo} cannot be returned for the specified uri. + * + * @see #getContentUri(String, int); + */ + @Nullable + public ResourceInfo getResourceInfo(Uri uri) { + if (!"content".equals(uri.getScheme())) { + return null; + } + if (!AUTHORITY.equals(uri.getAuthority())) { + return null; + } + + final List<String> pathItems = uri.getPathSegments(); + try { + synchronized (mPackageIds) { + final String packageId = pathItems.get(0); + final int resId = Integer.parseInt(pathItems.get(1)); + for (String packageName : mPackageIds.keySet()) { + if (packageId.equals(mPackageIds.get(packageName))) { + return new ResourceInfo(packageName, resId); + } + } + } + } catch (Exception e) { + Log.v(TAG, "Could not get resource info. Reason: " + e.getMessage()); + } + return null; + } + + /** + * A holder for a resource's package name and id. + */ + public static final class ResourceInfo { + + public final String packageName; + public final int id; + + private ResourceInfo(String packageName, int id) { + this.packageName = Objects.requireNonNull(packageName); + this.id = id; + } + } +} diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index c4d034207449..ed9b01916657 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -5212,6 +5212,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A updateReportedVisibilityLocked(); } + void onStartingWindowDrawn() { + if (task != null) { + task.setHasBeenVisible(true); + } + } + /** Called when the windows associated app window container are drawn. */ void onWindowsDrawn(boolean drawn, long timestampNs) { mDrawn = drawn; @@ -7362,7 +7368,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } final ActivityStack stack = getRootTask(); return stack != null && - stack.checkKeyguardVisibility(this, true /* shouldBeVisible */, true /* isTop */); + stack.checkKeyguardVisibility(this, true /* shouldBeVisible */, + stack.topRunningActivity() == this /* isTop */); } void setTurnScreenOn(boolean turnScreenOn) { diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index d777f3fee1a2..4e85837e9616 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.app.ActivityManager.START_SUCCESS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL; @@ -191,8 +192,9 @@ public class ActivityStartController { try { // TODO(multi-display-area): Support starting home in a task display area // Make sure home stack exist on display. + // TODO(b/153624902): Replace with TaskDisplayArea#getOrCreateRootHomeTask() homeStack = display.getDefaultTaskDisplayArea().getOrCreateStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, ON_TOP); } finally { mSupervisor.endDeferResume(); } diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 78ee1de78079..6f1ddcd793a9 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -403,11 +403,18 @@ public class AppTransition implements Dump { mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN; } - boolean isNextAppTransitionOpenCrossProfileApps() { return mNextAppTransitionType == NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS; } + boolean isNextAppTransitionCustomFromRecents() { + final RecentTasks recentTasks = mService.mAtmService.getRecentTasks(); + final String recentsPackageName = + (recentTasks != null) ? recentTasks.getRecentsComponent().getPackageName() : null; + return mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM + && mNextAppTransitionPackage.equals(recentsPackageName); + } + /** * @return true if and only if we are currently fetching app transition specs from the future * passed into {@link #overridePendingAppTransitionMultiThumbFuture} diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index a031fe82d48b..0a9878dd660b 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -442,10 +442,6 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // Always prepare an app transition since we rely on the transition callbacks to cleanup mWindowManager.prepareAppTransition(TRANSIT_NONE, false); controller.setCancelOnNextTransitionStart(); - } else { - // Just cancel directly to unleash from launcher when the next launching task is the - // current top task. - mWindowManager.cancelRecentsAnimation(REORDER_KEEP_IN_PLACE, "stackOrderChanged"); } } diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 6fda1170a3f5..54210ae1c0b0 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -43,6 +43,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IntArray; import android.util.Slog; import android.util.SparseBooleanArray; import android.util.proto.ProtoOutputStream; @@ -99,6 +100,8 @@ public class RecentsAnimationController implements DeathRecipient { private IRecentsAnimationRunner mRunner; private final RecentsAnimationCallbacks mCallbacks; private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>(); + private final IntArray mPendingNewTaskTargets = new IntArray(0); + private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations = new ArrayList<>(); private final int mDisplayId; @@ -220,6 +223,10 @@ public class RecentsAnimationController implements DeathRecipient { if (mCanceled) { return; } + // Remove all new task targets. + for (int i = mPendingNewTaskTargets.size() - 1; i >= 0; i--) { + removeTaskInternal(mPendingNewTaskTargets.get(i)); + } } // Note, the callback will handle its own synchronization, do not lock on WM lock @@ -310,6 +317,18 @@ public class RecentsAnimationController implements DeathRecipient { mWillFinishToHome = willFinishToHome; } } + + @Override + public boolean removeTask(int taskId) { + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mService.getWindowManagerLock()) { + return removeTaskInternal(taskId); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } }; /** @@ -405,11 +424,17 @@ public class RecentsAnimationController implements DeathRecipient { @VisibleForTesting AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) { + return addAnimation(task, isRecentTaskInvisible, null /* finishedCallback */); + } + + @VisibleForTesting + AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible, + OnAnimationFinishedCallback finishedCallback) { ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addAnimation(%s)", task.getName()); final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task, isRecentTaskInvisible); task.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */, - ANIMATION_TYPE_RECENTS); + ANIMATION_TYPE_RECENTS, finishedCallback); task.commitPendingTransaction(); mPendingAnimations.add(taskAdapter); return taskAdapter; @@ -489,6 +514,49 @@ public class RecentsAnimationController implements DeathRecipient { } } + void addTaskToTargets(Task task, OnAnimationFinishedCallback finishedCallback) { + if (mRunner != null) { + final RemoteAnimationTarget target = createTaskRemoteAnimation(task, finishedCallback); + if (target == null) return; + + ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addTaskToTargets, target: %s", target); + try { + mRunner.onTaskAppeared(target); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to report task appeared", e); + } + } + } + + private RemoteAnimationTarget createTaskRemoteAnimation(Task task, + OnAnimationFinishedCallback finishedCallback) { + final SparseBooleanArray recentTaskIds = + mService.mAtmService.getRecentTasks().getRecentTaskIds(); + TaskAnimationAdapter adapter = (TaskAnimationAdapter) addAnimation(task, + !recentTaskIds.get(task.mTaskId), finishedCallback); + mPendingNewTaskTargets.add(task.mTaskId); + return adapter.createRemoteAnimationTarget(); + } + + private boolean removeTaskInternal(int taskId) { + boolean result = false; + for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { + // Only allows when task target has became visible to user, to prevent + // the flickering during remove animation and task visible. + final TaskAnimationAdapter target = mPendingAnimations.get(i); + if (target.mTask.mTaskId == taskId && target.mTask.isOnTop()) { + removeAnimation(target); + final int taskIndex = mPendingNewTaskTargets.indexOf(taskId); + if (taskIndex != -1) { + mPendingNewTaskTargets.remove(taskIndex); + } + result = true; + break; + } + } + return result; + } + private RemoteAnimationTarget[] createAppAnimations() { final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>(); for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 80b8b5854966..1f6170a51b91 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -23,7 +23,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; @@ -38,6 +37,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE; import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; @@ -66,6 +66,7 @@ import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_KEEP_SCREEN_ON; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC; import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS; +import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static com.android.server.wm.RootWindowContainerProto.IS_HOME_RECENTS_COMPONENT; import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER; import static com.android.server.wm.RootWindowContainerProto.PENDING_ACTIVITIES; @@ -1369,8 +1370,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> calculateDefaultMinimalSizeOfResizeableTasks(); final TaskDisplayArea defaultTaskDisplayArea = getDefaultTaskDisplayArea(); - defaultTaskDisplayArea.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, - ON_TOP); + defaultTaskDisplayArea.getOrCreateRootHomeTask(); positionChildAt(POSITION_TOP, defaultTaskDisplayArea.mDisplayContent, false /* includingParents */); } @@ -1515,6 +1515,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Updates the extra information of the intent. if (fromHomeKey) { homeIntent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, true); + mWindowManager.cancelRecentsAnimation(REORDER_KEEP_IN_PLACE, "startHomeActivity"); } // Update the reason for ANR debugging to verify if the user activity is the one that // actually launched. @@ -2118,16 +2119,19 @@ class RootWindowContainer extends WindowContainer<DisplayContent> try { final Task task = r.getTask(); - final ActivityStack pinnedStack = taskDisplayArea.getRootPinnedTask(); + // This will change the pinned stack's windowing mode to its original mode, ensuring // we only have one stack that is in pinned mode. if (pinnedStack != null) { pinnedStack.dismissPip(); } - final boolean singleActivity = task.getChildCount() == 1; + // Set a transition to ensure that we don't immediately try and update the visibility + // of the activity entering PIP + r.getDisplayContent().prepareAppTransition(TRANSIT_NONE, false); + final boolean singleActivity = task.getChildCount() == 1; final ActivityStack stack; if (singleActivity) { stack = r.getRootTask(); @@ -2150,11 +2154,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mService.continueWindowLayout(); } - // TODO: revisit the following statement after the animation is moved from WM to SysUI. - // Update the visibility of all activities after the they have been reparented to the new - // stack. This MUST run after the animation above is scheduled to ensure that the windows - // drawn signal is scheduled after the bounds animation start call on the bounds animator - // thread. ensureActivitiesVisible(null, 0, false /* preserveWindows */); resumeFocusedStacksTopActivities(); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index d31939dec509..fd32724d82a3 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -86,6 +86,8 @@ import static com.android.server.wm.IdentifierProto.HASH_CODE; import static com.android.server.wm.IdentifierProto.TITLE; import static com.android.server.wm.IdentifierProto.USER_ID; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; +import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; +import static com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowContainerChildProto.TASK; @@ -135,6 +137,7 @@ import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.Surface; import android.view.SurfaceControl; +import android.view.WindowManager; import android.window.ITaskOrganizer; import com.android.internal.annotations.VisibleForTesting; @@ -2334,32 +2337,30 @@ class Task extends WindowContainer<WindowContainer> { return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize); } - private void resolveOrganizedOverrideConfiguration(Configuration newParentConfig) { + @Override + void resolveOverrideConfiguration(Configuration newParentConfig) { + mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds()); super.resolveOverrideConfiguration(newParentConfig); - if (!isOrganized()) { - return; - } - final Task root = getRootTask(); - if (root == this) { - return; + // Resolve override windowing mode to fullscreen for home task (even on freeform + // display), or split-screen-secondary if in split-screen mode. + int windowingMode = + getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode(); + if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) { + windowingMode = inSplitScreenWindowingMode() ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY + : WINDOWING_MODE_FULLSCREEN; + getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode); } - // Ensure to have the same windowing mode for the child tasks that controlled by task org. - getResolvedOverrideConfiguration().windowConfiguration - .setWindowingMode(root.getWindowingMode()); - } - - @Override - void resolveOverrideConfiguration(Configuration newParentConfig) { - if (!isLeafTask() || mCreatedByOrganizer) { - resolveOrganizedOverrideConfiguration(newParentConfig); + if (!isLeafTask()) { + // Compute configuration overrides for tasks that created by organizer, so that + // organizer can get the correct configuration from those tasks. + if (mCreatedByOrganizer) { + computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig); + } return; } - mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds()); - resolveOrganizedOverrideConfiguration(newParentConfig); - int windowingMode = - getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode(); + if (windowingMode == WINDOWING_MODE_UNDEFINED) { windowingMode = newParentConfig.windowConfiguration.getWindowingMode(); } @@ -3404,6 +3405,24 @@ class Task extends WindowContainer<WindowContainer> { } @Override + protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter, + int transit, boolean isVoiceInteraction, + @Nullable OnAnimationFinishedCallback finishedCallback) { + final RecentsAnimationController control = mWmService.getRecentsAnimationController(); + if (control != null && enter + && getDisplayContent().mAppTransition.isNextAppTransitionCustomFromRecents()) { + ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, + "addTaskToRecentsAnimationIfNeeded, control: %s, task: %s, transit: %s", + control, asTask(), AppTransition.appTransitionToString(transit)); + // We let the transition to be controlled by RecentsAnimation, and callback task's + // RemoteAnimationTarget for remote runner to animate. + control.addTaskToTargets(getRootTask(), finishedCallback); + } else { + super.applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, finishedCallback); + } + } + + @Override void dump(PrintWriter pw, String prefix, boolean dumpAll) { super.dump(pw, prefix, dumpAll); final String doublePrefix = prefix + " "; @@ -4100,8 +4119,18 @@ class Task extends WindowContainer<WindowContainer> { } void setHasBeenVisible(boolean hasBeenVisible) { + final boolean prevHasBeenVisible = mHasBeenVisible; mHasBeenVisible = hasBeenVisible; if (hasBeenVisible) { + // If the task is not yet visible when it is added to the task organizer, then we should + // hide it to allow the task organizer to show it when it is properly reparented. We + // skip this for tasks created by the organizer because they can synchronously update + // the leash before new children are added to the task. + if (!mCreatedByOrganizer && mTaskOrganizer != null && !prevHasBeenVisible) { + getPendingTransaction().hide(getSurfaceControl()); + commitPendingTransaction(); + } + sendTaskAppeared(); if (!isRootTask()) { getRootTask().setHasBeenVisible(true); @@ -4145,6 +4174,8 @@ class Task extends WindowContainer<WindowContainer> { // Let the old organizer know it has lost control. sendTaskVanished(); mTaskOrganizer = organizer; + + sendTaskAppeared(); onTaskOrganizerChanged(); return true; diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 11cfad263fb2..fee1c8019a07 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -1415,8 +1415,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { ActivityStack homeTask = getRootHomeTask(); if (homeTask == null && mDisplayContent.supportsSystemDecorations() && !mDisplayContent.isUntrustedVirtualDisplay()) { - homeTask = createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, - false /* onTop */); + homeTask = createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, false /* onTop */); } return homeTask; } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 22702dd6b566..9873031e0138 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -25,6 +25,7 @@ import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFI import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS; import android.annotation.Nullable; +import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.WindowConfiguration; import android.content.Intent; @@ -38,6 +39,7 @@ import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; import android.window.WindowContainerToken; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import java.io.PrintWriter; @@ -46,6 +48,7 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.WeakHashMap; +import java.util.function.Consumer; /** * Stores the TaskOrganizers associated with a given windowing mode and @@ -81,17 +84,105 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } } - }; + } + + /** + * A wrapper class around ITaskOrganizer to ensure that the calls are made in the right + * lifecycle order since we may be updating the visibility of task surface controls in a pending + * transaction before they are presented to the task org. + */ + private class TaskOrganizerCallbacks { + final WindowManagerService mService; + final ITaskOrganizer mTaskOrganizer; + final Consumer<Runnable> mDeferTaskOrgCallbacksConsumer; + + TaskOrganizerCallbacks(WindowManagerService wm, ITaskOrganizer taskOrg, + Consumer<Runnable> deferTaskOrgCallbacksConsumer) { + mService = wm; + mDeferTaskOrgCallbacksConsumer = deferTaskOrgCallbacksConsumer; + mTaskOrganizer = taskOrg; + } + + IBinder getBinder() { + return mTaskOrganizer.asBinder(); + } + + void onTaskAppeared(Task task) { + final RunningTaskInfo taskInfo = task.getTaskInfo(); + mDeferTaskOrgCallbacksConsumer.accept(() -> { + try { + mTaskOrganizer.onTaskAppeared(taskInfo); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onTaskAppeared callback", e); + } + }); + } + + + void onTaskVanished(Task task) { + final RunningTaskInfo taskInfo = task.getTaskInfo(); + mDeferTaskOrgCallbacksConsumer.accept(() -> { + try { + mTaskOrganizer.onTaskVanished(taskInfo); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onTaskVanished callback", e); + } + }); + } + + void onTaskInfoChanged(Task task, ActivityManager.RunningTaskInfo taskInfo) { + if (!task.mCreatedByOrganizer && !task.mTaskAppearedSent) { + // Skip if the task has not yet received taskAppeared(), except for tasks created + // by the organizer that don't receive that signal + return; + } + mDeferTaskOrgCallbacksConsumer.accept(() -> { + if (!task.isOrganized()) { + // This is safe to ignore if the task is no longer organized + return; + } + try { + mTaskOrganizer.onTaskInfoChanged(taskInfo); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onTaskInfoChanged callback", e); + } + }); + } + + void onBackPressedOnTaskRoot(Task task) { + if (!task.mCreatedByOrganizer && !task.mTaskAppearedSent) { + // Skip if the task has not yet received taskAppeared(), except for tasks created + // by the organizer that don't receive that signal + return; + } + mDeferTaskOrgCallbacksConsumer.accept(() -> { + if (!task.isOrganized()) { + // This is safe to ignore if the task is no longer organized + return; + } + try { + mTaskOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo()); + } catch (Exception e) { + Slog.e(TAG, "Exception sending onBackPressedOnTaskRoot callback", e); + } + }); + } + } private class TaskOrganizerState { - private final ITaskOrganizer mOrganizer; + private final TaskOrganizerCallbacks mOrganizer; private final DeathRecipient mDeathRecipient; private final ArrayList<Task> mOrganizedTasks = new ArrayList<>(); private final int mUid; private boolean mInterceptBackPressedOnTaskRoot; TaskOrganizerState(ITaskOrganizer organizer, int uid) { - mOrganizer = organizer; + final Consumer<Runnable> deferTaskOrgCallbacksConsumer = + mDeferTaskOrgCallbacksConsumer != null + ? mDeferTaskOrgCallbacksConsumer + : mService.mWindowManager.mAnimator::addAfterPrepareSurfacesRunnable; + mOrganizer = new TaskOrganizerCallbacks(mService.mWindowManager, organizer, + deferTaskOrgCallbacksConsumer); mDeathRecipient = new DeathRecipient(organizer); try { organizer.asBinder().linkToDeath(mDeathRecipient, 0); @@ -112,23 +203,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mOrganizedTasks.add(t); } if (t.taskAppearedReady()) { - try { - t.mTaskAppearedSent = true; - mOrganizer.onTaskAppeared(t.getTaskInfo()); - } catch (Exception e) { - Slog.e(TAG, "Exception sending taskAppeared callback" + e); - } + t.mTaskAppearedSent = true; + mOrganizer.onTaskAppeared(t); } } void removeTask(Task t) { if (t.mTaskAppearedSent) { - try { - t.mTaskAppearedSent = false; - mOrganizer.onTaskVanished(t.getTaskInfo()); - } catch (Exception e) { - Slog.e(TAG, "Exception sending taskVanished callback" + e); - } + t.mTaskAppearedSent = false; + mOrganizer.onTaskVanished(t); } mOrganizedTasks.remove(t); } @@ -136,7 +219,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { void dispose() { releaseTasks(); for (int i = mTaskOrganizersForWindowingMode.size() - 1; i >= 0; --i) { - mTaskOrganizersForWindowingMode.valueAt(i).remove(mOrganizer.asBinder()); + mTaskOrganizersForWindowingMode.valueAt(i).remove(mOrganizer.getBinder()); } } @@ -149,7 +232,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } void unlinkDeath() { - mOrganizer.asBinder().unlinkToDeath(mDeathRecipient, 0); + mOrganizer.getBinder().unlinkToDeath(mDeathRecipient, 0); } } @@ -159,9 +242,10 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>(); private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>(); - final ActivityTaskManagerService mService; + private final ActivityTaskManagerService mService; - RunningTaskInfo mTmpTaskInfo; + private RunningTaskInfo mTmpTaskInfo; + private Consumer<Runnable> mDeferTaskOrgCallbacksConsumer; TaskOrganizerController(ActivityTaskManagerService atm) { mService = atm; @@ -173,6 +257,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } /** + * Specifies the consumer to run to defer the task org callbacks. Can be overridden while + * testing to allow the callbacks to be sent synchronously. + */ + @VisibleForTesting + public void setDeferTaskOrgCallbacksConsumer(Consumer<Runnable> consumer) { + mDeferTaskOrgCallbacksConsumer = consumer; + } + + /** * Register a TaskOrganizer to manage tasks as they enter the given windowing mode. * If there was already a TaskOrganizer for this windowing mode it will be evicted * but will continue to organize it's existing tasks. @@ -263,7 +356,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { if (state == null) { return null; } - return state.mOrganizer; + return state.mOrganizer.mTaskOrganizer; } void onTaskAppeared(ITaskOrganizer organizer, Task task) { @@ -368,10 +461,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { // change. mTmpTaskInfo = null; - if (task.mTaskOrganizer != null) { - try { - task.mTaskOrganizer.onTaskInfoChanged(newInfo); - } catch (RemoteException e) { + if (task.isOrganized()) { + // Because we defer sending taskAppeared() until the app has drawn, we may receive a + // configuration change before the state actually has the task registered. As such we + // should ignore these change events to the organizer until taskAppeared(). If the task + // was created by the organizer, then we always send the info change. + final TaskOrganizerState state = mTaskOrganizerStates.get( + task.mTaskOrganizer.asBinder()); + if (state != null) { + state.mOrganizer.onTaskInfoChanged(task, newInfo); } } } @@ -531,11 +629,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return false; } - try { - state.mOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo()); - } catch (Exception e) { - Slog.e(TAG, "Exception sending interceptBackPressedOnTaskRoot callback" + e); - } + state.mOrganizer.onBackPressedOnTaskRoot(task); return true; } @@ -552,7 +646,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { final TaskOrganizerState state = mTaskOrganizerStates.get(taskOrgs.get(j)); final ArrayList<Task> tasks = state.mOrganizedTasks; pw.print(innerPrefix + " "); - pw.println(state.mOrganizer + " uid=" + state.mUid + ":"); + pw.println(state.mOrganizer.mTaskOrganizer + " uid=" + state.mUid + ":"); for (int k = 0; k < tasks.size(); k++) { pw.println(innerPrefix + " " + tasks.get(k)); } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index f6e952c4cea1..fba22dd4e9df 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2076,8 +2076,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * @see #getAnimationAdapter */ boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, - boolean isVoiceInteraction, - @Nullable OnAnimationFinishedCallback animationFinishedCallback) { + boolean isVoiceInteraction, @Nullable OnAnimationFinishedCallback finishedCallback) { if (mWmService.mDisableTransitionAnimation) { ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: transition animation is disabled or skipped. " @@ -2092,22 +2091,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WC#applyAnimation"); if (okToAnimate()) { - final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp, - transit, enter, isVoiceInteraction); - AnimationAdapter adapter = adapters.first; - AnimationAdapter thumbnailAdapter = adapters.second; - if (adapter != null) { - startAnimation(getPendingTransaction(), adapter, !isVisible(), - ANIMATION_TYPE_APP_TRANSITION, animationFinishedCallback); - if (adapter.getShowWallpaper()) { - getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; - } - if (thumbnailAdapter != null) { - mSurfaceFreezer.mSnapshot.startAnimation(getPendingTransaction(), - thumbnailAdapter, ANIMATION_TYPE_APP_TRANSITION, - (type, anim) -> { }); - } - } + applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, finishedCallback); } else { cancelAnimation(); } @@ -2201,12 +2185,37 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return resultAdapters; } + protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter, + int transit, boolean isVoiceInteraction, + @Nullable OnAnimationFinishedCallback finishedCallback) { + final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp, + transit, enter, isVoiceInteraction); + AnimationAdapter adapter = adapters.first; + AnimationAdapter thumbnailAdapter = adapters.second; + if (adapter != null) { + startAnimation(getPendingTransaction(), adapter, !isVisible(), + ANIMATION_TYPE_APP_TRANSITION, finishedCallback); + if (adapter.getShowWallpaper()) { + getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + } + if (thumbnailAdapter != null) { + mSurfaceFreezer.mSnapshot.startAnimation(getPendingTransaction(), + thumbnailAdapter, ANIMATION_TYPE_APP_TRANSITION, (type, anim) -> { }); + } + } + } + final SurfaceAnimationRunner getSurfaceAnimationRunner() { return mWmService.mSurfaceAnimationRunner; } private Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, boolean isVoiceInteraction) { + if (isOrganized()) { + // Defer to the task organizer to run animations + return null; + } + final DisplayContent displayContent = getDisplayContent(); final DisplayInfo displayInfo = displayContent.getDisplayInfo(); final int width = displayInfo.appWidth; @@ -2577,4 +2586,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return willSync; } + + boolean useBLASTSync() { + return mUsingBLASTSyncTransaction; + } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 513187dc17f5..c3fb68f9e0c4 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -73,6 +73,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY; +import static android.view.WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC; import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; @@ -2110,6 +2111,10 @@ public class WindowManagerService extends IWindowManager.Stub win.finishSeamlessRotation(false /* timeout */); } + if (win.useBLASTSync()) { + result |= RELAYOUT_RES_BLAST_SYNC; + } + int attrChanges = 0; int flagChanges = 0; int privateFlagChanges = 0; @@ -8019,6 +8024,33 @@ public class WindowManagerService extends IWindowManager.Stub } } + /** Set layer tracing flags. */ + public void setLayerTracingFlags(int flags) { + mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP, + "setLayerTracingFlags"); + long token = Binder.clearCallingIdentity(); + try { + Parcel data = null; + try { + IBinder sf = ServiceManager.getService("SurfaceFlinger"); + if (sf != null) { + data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + data.writeInt(flags); + sf.transact(1033 /* LAYER_TRACE_FLAGS_CODE */, data, null, 0 /* flags */); + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to set layer tracing flags"); + } finally { + if (data != null) { + data.recycle(); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + @Override public boolean mirrorDisplay(int displayId, SurfaceControl outSurfaceControl) { if (!checkCallingPermission(READ_FRAME_BUFFER, "mirrorDisplay()")) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 5a76bac67d64..627fdc342a9a 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -4274,9 +4274,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP logPerformShow("performShow on "); final int drawState = mWinAnimator.mDrawState; - if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW) - && mAttrs.type != TYPE_APPLICATION_STARTING && mActivityRecord != null) { - mActivityRecord.onFirstWindowDrawn(this, mWinAnimator); + if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW) && mActivityRecord != null) { + if (mAttrs.type != TYPE_APPLICATION_STARTING) { + mActivityRecord.onFirstWindowDrawn(this, mWinAnimator); + } else { + mActivityRecord.onStartingWindowDrawn(); + } } if (mWinAnimator.mDrawState != READY_TO_SHOW || !isReadyForDisplay()) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index d1d353dfbf39..1da074002456 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -2702,10 +2702,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Slog.i(LOG_TAG, "Clearing the DO..."); final ComponentName doAdminReceiver = doAdmin.info.getComponent(); clearDeviceOwnerLocked(doAdmin, doUserId); - // TODO(b/143516163): If we have a power cut here, we might leave active admin. Consider if - // it is worth the complexity to make it more robust. Slog.i(LOG_TAG, "Removing admin artifacts..."); - // TODO(b/143516163): Clean up application restrictions in UserManager. + // TODO(b/149075700): Clean up application restrictions in UserManager. removeAdminArtifacts(doAdminReceiver, doUserId); Slog.i(LOG_TAG, "Migration complete."); @@ -2747,18 +2745,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // The following policies weren't available to PO, but will be available after migration. parentAdmin.disableCamera = doAdmin.disableCamera; - parentAdmin.requireAutoTime = doAdmin.requireAutoTime; - - // TODO(b/143516163): Uncomment once corresponding APIs are available via parent instance. - // parentAdmin.disableScreenCapture = doAdmin.disableScreenCapture; - // parentAdmin.accountTypesWithManagementDisabled.addAll( - // doAdmin.accountTypesWithManagementDisabled); + parentAdmin.disableScreenCapture = doAdmin.disableScreenCapture; + parentAdmin.accountTypesWithManagementDisabled.addAll( + doAdmin.accountTypesWithManagementDisabled); moveDoUserRestrictionsToCopeParent(doAdmin, parentAdmin); - - // TODO(b/143516163): migrate network and security logging state, currently they are - // turned off when DO is removed. } private void moveDoUserRestrictionsToCopeParent(ActiveAdmin doAdmin, ActiveAdmin parentAdmin) { @@ -2778,7 +2770,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * a managed profile. */ @GuardedBy("getLockObject()") - void applyManagedProfileRestrictionIfDeviceOwnerLocked() { + private void applyManagedProfileRestrictionIfDeviceOwnerLocked() { final int doUserId = mOwners.getDeviceOwnerUserId(); if (doUserId == UserHandle.USER_NULL) { logIfVerbose("No DO found, skipping application of restriction."); @@ -4002,11 +3994,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mOwners.systemReady(); break; case SystemService.PHASE_ACTIVITY_MANAGER_READY: - maybeStartSecurityLogMonitorOnActivityManagerReady(); synchronized (getLockObject()) { migrateToProfileOnOrganizationOwnedDeviceIfCompLocked(); applyManagedProfileRestrictionIfDeviceOwnerLocked(); } + maybeStartSecurityLogMonitorOnActivityManagerReady(); final int userId = getManagedUserId(UserHandle.USER_SYSTEM); if (userId >= 0) { updatePersonalAppSuspension(userId, false /* running */); diff --git a/services/tests/servicestests/res/raw/comp_policies_primary.xml b/services/tests/servicestests/res/raw/comp_policies_primary.xml index 8b7709e0a14f..395b8ab4ba75 100644 --- a/services/tests/servicestests/res/raw/comp_policies_primary.xml +++ b/services/tests/servicestests/res/raw/comp_policies_primary.xml @@ -5,5 +5,9 @@ <password-history-length value="33" /> <require_auto_time value="true" /> <user-restrictions no_bluetooth="true" /> + <disable-screen-capture value="true" /> + <disable-account-management> + <account-type value="com.google-primary" /> + </disable-account-management> </admin> </policies> diff --git a/services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml b/services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml index c874dcca2c73..c65d05693f10 100644 --- a/services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml +++ b/services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml @@ -2,5 +2,8 @@ <policies setup-complete="true" provisioning-state="3"> <admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1"> <policies flags="991"/> + <disable-account-management> + <account-type value="com.google-profile" /> + </disable-account-management> </admin> </policies> diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java index c9bd01a31af0..74e7f8c44d1a 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java @@ -19,6 +19,7 @@ import static android.os.UserHandle.USER_SYSTEM; import static com.android.server.devicepolicy.DpmTestUtils.writeInputStreamToFile; +import static org.junit.Assert.assertArrayEquals; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; @@ -378,6 +379,15 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { 33, dpm.getParentProfileInstance(admin1).getPasswordHistoryLength(admin1)); assertEquals("Password history policy was put into non-parent PO instance", 0, dpm.getPasswordHistoryLength(admin1)); + assertTrue("Screen capture restriction wasn't migrated to PO parent instance", + dpm.getParentProfileInstance(admin1).getScreenCaptureDisabled(admin1)); + + assertArrayEquals("Accounts with management disabled weren't migrated to PO parent", + new String[] {"com.google-primary"}, + dpm.getParentProfileInstance(admin1).getAccountTypesWithManagementDisabled()); + assertArrayEquals("Accounts with management disabled for profile were lost", + new String[] {"com.google-profile"}, + dpm.getAccountTypesWithManagementDisabled()); assertTrue("User restriction wasn't migrated to PO parent instance", dpm.getParentProfileInstance(admin1).getUserRestrictions(admin1) @@ -394,7 +404,6 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { dpms.getProfileOwnerAdminLocked(COPE_PROFILE_USER_ID) .getEffectiveRestrictions() .containsKey(UserManager.DISALLOW_CONFIG_DATE_TIME)); - // TODO(b/143516163): verify more policies. }); } diff --git a/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java b/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java new file mode 100644 index 000000000000..72580a3b98c2 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.textclassifier; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.net.Uri; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Sanity test for {@link IconsContentProvider}. + */ +@RunWith(AndroidJUnit4.class) +public final class IconsContentProviderTest { + + @Test + public void testLoadResource() { + final Context context = ApplicationProvider.getApplicationContext(); + // Testing with the android package name because this is the only package name + // that returns the same uri across multiple classloaders. + final String packageName = "android"; + final int resId = android.R.drawable.btn_star; + final Uri uri = IconsUriHelper.getInstance().getContentUri(packageName, resId); + + final Drawable expected = Icon.createWithResource(packageName, resId).loadDrawable(context); + // Ensure we are testing with a non-empty image. + assertThat(expected.getIntrinsicWidth()).isGreaterThan(0); + assertThat(expected.getIntrinsicHeight()).isGreaterThan(0); + + final Drawable actual = Icon.createWithContentUri(uri).loadDrawable(context); + assertThat(actual).isNotNull(); + assertThat(IconsContentProvider.getBitmapData(actual)) + .isEqualTo(IconsContentProvider.getBitmapData(expected)); + } + + @Test + public void testLoadResource_badUri() { + final Uri badUri = new Uri.Builder() + .scheme("content") + .authority(IconsUriHelper.AUTHORITY) + .path("badPackageId") + .appendPath("1234") + .build(); + + final Context context = ApplicationProvider.getApplicationContext(); + assertThat(Icon.createWithContentUri(badUri).loadDrawable(context)).isNull(); + } +} + diff --git a/services/tests/servicestests/src/com/android/server/textclassifier/IconsUriHelperTest.java b/services/tests/servicestests/src/com/android/server/textclassifier/IconsUriHelperTest.java new file mode 100644 index 000000000000..96f09d965b13 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/textclassifier/IconsUriHelperTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.textclassifier; + +import static com.google.common.truth.Truth.assertThat; + +import android.net.Uri; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.textclassifier.IconsUriHelper.ResourceInfo; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for {@link IconsUriHelper}. + */ +@RunWith(AndroidJUnit4.class) +public final class IconsUriHelperTest { + + private IconsUriHelper mIconsUriHelper; + + @Before + public void setUp() { + mIconsUriHelper = IconsUriHelper.newInstanceForTesting(null); + } + + @Test + public void testGetContentUri() { + final IconsUriHelper iconsUriHelper = IconsUriHelper.newInstanceForTesting(() -> "pkgId"); + final Uri expected = new Uri.Builder() + .scheme("content") + .authority(IconsUriHelper.AUTHORITY) + .path("pkgId") + .appendPath("1234") + .build(); + + final Uri actual = iconsUriHelper.getContentUri("com.package.name", 1234); + assertThat(actual).isEqualTo(expected); + } + + @Test + public void testGetContentUri_multiplePackages() { + final Uri uri1 = mIconsUriHelper.getContentUri("com.package.name1", 1234); + final Uri uri2 = mIconsUriHelper.getContentUri("com.package.name2", 5678); + + assertThat(uri1.getScheme()).isEqualTo("content"); + assertThat(uri2.getScheme()).isEqualTo("content"); + + assertThat(uri1.getAuthority()).isEqualTo(IconsUriHelper.AUTHORITY); + assertThat(uri2.getAuthority()).isEqualTo(IconsUriHelper.AUTHORITY); + + assertThat(uri1.getPathSegments().get(1)).isEqualTo("1234"); + assertThat(uri2.getPathSegments().get(1)).isEqualTo("5678"); + } + + @Test + public void testGetContentUri_samePackageIdForSamePackageName() { + final String packageName = "com.package.name"; + final Uri uri1 = mIconsUriHelper.getContentUri(packageName, 1234); + final Uri uri2 = mIconsUriHelper.getContentUri(packageName, 5678); + + final String id1 = uri1.getPathSegments().get(0); + final String id2 = uri2.getPathSegments().get(0); + + assertThat(id1).isEqualTo(id2); + } + + @Test + public void testGetResourceInfo() { + mIconsUriHelper.getContentUri("com.package.name1", 123); + final Uri uri = mIconsUriHelper.getContentUri("com.package.name2", 456); + mIconsUriHelper.getContentUri("com.package.name3", 789); + + final ResourceInfo res = mIconsUriHelper.getResourceInfo(uri); + assertThat(res.packageName).isEqualTo("com.package.name2"); + assertThat(res.id).isEqualTo(456); + } + + @Test + public void testGetResourceInfo_unrecognizedUri() { + final Uri uri = new Uri.Builder() + .scheme("content") + .authority(IconsUriHelper.AUTHORITY) + .path("unrecognized") + .appendPath("1234") + .build(); + assertThat(mIconsUriHelper.getResourceInfo(uri)).isNull(); + } + + @Test + public void testGetResourceInfo_invalidScheme() { + final IconsUriHelper iconsUriHelper = IconsUriHelper.newInstanceForTesting(() -> "pkgId"); + iconsUriHelper.getContentUri("com.package.name", 1234); + + final Uri uri = new Uri.Builder() + .scheme("file") + .authority(IconsUriHelper.AUTHORITY) + .path("pkgId") + .appendPath("1234") + .build(); + assertThat(iconsUriHelper.getResourceInfo(uri)).isNull(); + } + + @Test + public void testGetResourceInfo_invalidAuthority() { + final IconsUriHelper iconsUriHelper = IconsUriHelper.newInstanceForTesting(() -> "pkgId"); + iconsUriHelper.getContentUri("com.package.name", 1234); + + final Uri uri = new Uri.Builder() + .scheme("content") + .authority("invalid.authority") + .path("pkgId") + .appendPath("1234") + .build(); + assertThat(iconsUriHelper.getResourceInfo(uri)).isNull(); + } +} + diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 327cfc74dac5..39062f017a73 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -818,6 +818,69 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_RESTRICTED); } + /** + * Test that an app is "timed out" into the RESTRICTED bucket if prediction tries to put it into + * a low bucket after the RESTRICTED timeout. + */ + @Test + public void testRestrictedTimeoutOverridesRestoredLowBucketPrediction() { + reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); + assertBucket(STANDBY_BUCKET_ACTIVE); + + // Predict to RARE Not long enough to time out into RESTRICTED. + mInjector.mElapsedRealtime += RARE_THRESHOLD; + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, + REASON_MAIN_PREDICTED); + assertBucket(STANDBY_BUCKET_RARE); + + // Add a short timeout event + mInjector.mElapsedRealtime += 1000; + reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); + assertBucket(STANDBY_BUCKET_ACTIVE); + mInjector.mElapsedRealtime += 1000; + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_ACTIVE); + + // Long enough that it could have timed out into RESTRICTED. Instead of reverting to + // predicted RARE, should go into RESTRICTED + mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4; + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_RESTRICTED); + + // Ensure that prediction can still raise it out despite this override. + mInjector.mElapsedRealtime += 1; + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, + REASON_MAIN_PREDICTED); + assertBucket(STANDBY_BUCKET_ACTIVE); + } + + /** + * Test that an app is "timed out" into the RESTRICTED bucket if prediction tries to put it into + * a low bucket after the RESTRICTED timeout. + */ + @Test + public void testRestrictedTimeoutOverridesPredictionLowBucket() { + reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); + + // Not long enough to time out into RESTRICTED. + mInjector.mElapsedRealtime += RARE_THRESHOLD; + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, + REASON_MAIN_PREDICTED); + assertBucket(STANDBY_BUCKET_RARE); + + mInjector.mElapsedRealtime += 1; + reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); + + // Long enough that it could have timed out into RESTRICTED. + mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4; + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, + REASON_MAIN_PREDICTED); + assertBucket(STANDBY_BUCKET_ACTIVE); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, + REASON_MAIN_PREDICTED); + assertBucket(STANDBY_BUCKET_RESTRICTED); + } + @Test public void testPredictionRaiseFromRestrictedTimeout_highBucket() { reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java index 2077ecb2799e..96c69af036b6 100644 --- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java +++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java @@ -197,7 +197,8 @@ public class ShortcutManagerTestUtils { final String PREFIX = "Launcher: ComponentInfo{"; final String POSTFIX = "}"; final List<String> result = runShortcutCommandForSuccess( - instrumentation, "get-default-launcher"); + instrumentation, "get-default-launcher --user " + + instrumentation.getContext().getUserId()); for (String s : result) { if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) { return s.substring(PREFIX.length(), s.length() - POSTFIX.length()); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index 881561f5750b..1f6ba7adf114 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -238,9 +238,6 @@ public class RecentsAnimationTest extends ActivityTestsBase { assertTrue(targetActivity.mLaunchTaskBehind); anotherHomeActivity.moveFocusableActivityToTop("launchAnotherHome"); - // The current top activity is not the recents so the animation should be canceled. - verify(mService.mWindowManager, times(1)).cancelRecentsAnimation( - eq(REORDER_KEEP_IN_PLACE), any() /* reason */); // The test uses mocked RecentsAnimationController so we have to invoke the callback // manually to simulate the flow. @@ -279,10 +276,6 @@ public class RecentsAnimationTest extends ActivityTestsBase { fullscreenStack.moveToFront("Activity start"); - // Ensure that the recents animation was canceled by cancelAnimationSynchronously(). - verify(mService.mWindowManager, times(1)).cancelRecentsAnimation( - eq(REORDER_KEEP_IN_PLACE), any()); - // Assume recents animation already started, set a state that cancel recents animation // with screenshot. doReturn(true).when(mRecentsAnimationController).shouldDeferCancelUntilNextTransition(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java index 06ca6c110613..f275e378ed26 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java @@ -69,6 +69,8 @@ import android.window.WindowContainerTransaction; import androidx.test.filters.SmallTest; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -117,6 +119,13 @@ public class TaskOrganizerTests extends WindowTestsBase { return createTaskStackOnDisplay(mDisplayContent); } + @Before + public void setUp() { + // We defer callbacks since we need to adjust task surface visibility, but for these tests, + // just run the callbacks synchronously + mWm.mAtmService.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer((r) -> r.run()); + } + @Test public void testAppearVanish() throws RemoteException { final ActivityStack stack = createStack(); diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java index f019a9d33005..bce06e4777a2 100644 --- a/telecomm/java/android/telecom/Conference.java +++ b/telecomm/java/android/telecom/Conference.java @@ -74,6 +74,7 @@ public abstract class Conference extends Conferenceable { public void onConnectionEvent(Conference c, String event, Bundle extras) {} public void onCallerDisplayNameChanged( Conference c, String callerDisplayName, int presentation) {} + public void onCallDirectionChanged(Conference c, int callDirection) {} public void onRingbackRequested(Conference c, boolean ringback) {} } @@ -103,6 +104,7 @@ public abstract class Conference extends Conferenceable { private int mAddressPresentation; private String mCallerDisplayName; private int mCallerDisplayNamePresentation; + private int mCallDirection; private boolean mRingbackRequested = false; private final Connection.Listener mConnectionDeathListener = new Connection.Listener() { @@ -1024,6 +1026,25 @@ public abstract class Conference extends Conferenceable { } /** + * Sets the call direction of this {@link Conference}. By default, all {@link Conference}s have + * a direction of {@link android.telecom.Call.Details.CallDirection#DIRECTION_UNKNOWN}. The + * direction of a {@link Conference} is only applicable to the case where + * {@link #setConferenceState(boolean)} has been set to {@code false}, otherwise the direction + * will be ignored. + * @param callDirection The direction of the conference. + * @hide + */ + @RequiresPermission(MODIFY_PHONE_STATE) + public final void setCallDirection(@Call.Details.CallDirection int callDirection) { + Log.d(this, "setDirection %d", callDirection); + mCallDirection = callDirection; + for (Listener l : mListeners) { + l.onCallDirectionChanged(this, callDirection); + } + } + + + /** * Sets the address of this {@link Conference}. Used when {@link #setConferenceState(boolean)} * is called to mark a conference temporarily as NOT a conference. * <p> @@ -1102,6 +1123,15 @@ public abstract class Conference extends Conferenceable { } /** + * @return The call direction of this conference. Only applicable when + * {@link #setConferenceState(boolean)} is set to false. + * @hide + */ + public final @Call.Details.CallDirection int getCallDirection() { + return mCallDirection; + } + + /** * Sets the caller display name (CNAP) of this {@link Conference}. Used when * {@link #setConferenceState(boolean)} is called to mark a conference temporarily as NOT a * conference. diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index ffd25c08a8ba..0d66013d92a1 100755 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -1554,6 +1554,14 @@ public abstract class ConnectionService extends Service { } @Override + public void onCallDirectionChanged(Conference c, int direction) { + String id = mIdByConference.get(c); + if (id != null) { + mAdapter.setCallDirection(id, direction); + } + } + + @Override public void onAddressChanged(Conference c, Uri newAddress, int presentation) { String id = mIdByConference.get(c); if (id != null) { diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java index 8f273233044e..f8a6cf03934a 100644 --- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java +++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java @@ -693,4 +693,20 @@ final class ConnectionServiceAdapter implements DeathRecipient { } } } + + /** + * Sets the direction of a call. Setting a new direction of an existing call is usually only + * applicable during single caller emulation during conferencing, see + * {@link Conference#setConferenceState(boolean)} for more information. + * @param callId The identifier of the call. + * @param direction The new direction of the call. + */ + void setCallDirection(String callId, @Call.Details.CallDirection int direction) { + for (IConnectionServiceAdapter a : mAdapters) { + try { + a.setCallDirection(callId, direction, Log.getExternalSession()); + } catch (RemoteException e) { + } + } + } } diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java index 79ad51b92b81..6c1ea322e66e 100644 --- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java +++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java @@ -76,6 +76,7 @@ final class ConnectionServiceAdapterServant { private static final int MSG_CONNECTION_SERVICE_FOCUS_RELEASED = 35; private static final int MSG_SET_CONFERENCE_STATE = 36; private static final int MSG_HANDLE_CREATE_CONFERENCE_COMPLETE = 37; + private static final int MSG_SET_CALL_DIRECTION = 38; private final IConnectionServiceAdapter mDelegate; @@ -353,7 +354,7 @@ final class ConnectionServiceAdapterServant { case MSG_CONNECTION_SERVICE_FOCUS_RELEASED: mDelegate.onConnectionServiceFocusReleased(null /*Session.Info*/); break; - case MSG_SET_CONFERENCE_STATE: + case MSG_SET_CONFERENCE_STATE: { SomeArgs args = (SomeArgs) msg.obj; try { mDelegate.setConferenceState((String) args.arg1, (Boolean) args.arg2, @@ -361,6 +362,17 @@ final class ConnectionServiceAdapterServant { } finally { args.recycle(); } + break; + } + case MSG_SET_CALL_DIRECTION: { + SomeArgs args = (SomeArgs) msg.obj; + try { + mDelegate.setCallDirection((String) args.arg1, args.argi1, + (Session.Info) args.arg2); + } finally { + args.recycle(); + } + } } } }; @@ -670,6 +682,16 @@ final class ConnectionServiceAdapterServant { args.arg3 = sessionInfo; mHandler.obtainMessage(MSG_SET_CONFERENCE_STATE, args).sendToTarget(); } + + @Override + public void setCallDirection(String callId, int direction, + Session.Info sessionInfo) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.argi1 = direction; + args.arg2 = sessionInfo; + mHandler.obtainMessage(MSG_SET_CALL_DIRECTION, args).sendToTarget(); + } }; public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) { diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java index 76640e036eeb..cad5b707a146 100644 --- a/telecomm/java/android/telecom/RemoteConnectionService.java +++ b/telecomm/java/android/telecom/RemoteConnectionService.java @@ -485,6 +485,11 @@ final class RemoteConnectionService { Session.Info sessionInfo) { // Do nothing } + + @Override + public void setCallDirection(String callId, int direction, Session.Info sessionInfo) { + // Do nothing + } }; private final ConnectionServiceAdapterServant mServant = diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index c993cfad1d05..b974c567008f 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -1534,12 +1534,22 @@ public class TelecomManager { /** * Return the line 1 phone number for given phone account. * - * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE} + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_SMS READ_SMS}, + * {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS}, + * or that the caller is the default SMS app for any API level. + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * for apps targeting SDK API level 29 and below. * * @param accountHandle The handle for the account retrieve a number for. * @return A string representation of the line 1 phone number. */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges or default SMS app + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_SMS, + android.Manifest.permission.READ_PHONE_NUMBERS + }, conditional = true) public String getLine1Number(PhoneAccountHandle accountHandle) { try { if (isServiceConnected()) { diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl index 4f63e08abce6..3fd7f949cfe6 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl @@ -132,4 +132,6 @@ oneway interface IConnectionServiceAdapter { void resetConnectionTime(String callIdi, in Session.Info sessionInfo); void setConferenceState(String callId, boolean isConference, in Session.Info sessionInfo); + + void setCallDirection(String callId, int direction, in Session.Info sessionInfo); } diff --git a/telephony/java/android/telephony/CbGeoUtils.java b/telephony/java/android/telephony/CbGeoUtils.java index c0ae99e89cb3..806bac0c32c9 100644 --- a/telephony/java/android/telephony/CbGeoUtils.java +++ b/telephony/java/android/telephony/CbGeoUtils.java @@ -128,6 +128,23 @@ public class CbGeoUtils { public String toString() { return "(" + lat + "," + lng + ")"; } + + /** + * @hide + */ + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof LatLng)) { + return false; + } + + LatLng l = (LatLng) o; + return lat == l.lat && lng == l.lng; + } } /** @@ -280,6 +297,32 @@ public class CbGeoUtils { } return str; } + + /** + * @hide + */ + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof Polygon)) { + return false; + } + + Polygon p = (Polygon) o; + if (mVertices.size() != p.mVertices.size()) { + return false; + } + for (int i = 0; i < mVertices.size(); i++) { + if (!mVertices.get(i).equals(p.mVertices.get(i))) { + return false; + } + } + + return true; + } } /** @@ -335,6 +378,24 @@ public class CbGeoUtils { return str; } + + /** + * @hide + */ + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof Circle)) { + return false; + } + + Circle c = (Circle) o; + return mCenter.equals(c.mCenter) + && Double.compare(mRadiusMeter, c.mRadiusMeter) == 0; + } } /** diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index d6cdaa6d8bc0..f623649fb25e 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -147,6 +147,10 @@ import java.util.regex.Pattern; * information unless it has the appropriate permissions declared in * its manifest file. Where permissions apply, they are noted in the * the methods through which you access the protected information. + * + * <p>TelephonyManager is intended for use on devices that implement + * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. On devices + * that do not implement this feature, the behavior is not reliable. */ @SystemService(Context.TELEPHONY_SERVICE) public class TelephonyManager { @@ -5842,6 +5846,10 @@ public class TelephonyManager { * {@link android.telephony.PhoneStateListener#onCellInfoChanged onCellInfoChanged()} * for each active subscription. * + * <p>This method returns valid data for devices with + * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. On devices + * that do not implement this feature, the behavior is not reliable. + * * @param executor the executor on which callback will be invoked. * @param callback a callback to receive CellInfo. */ @@ -5888,6 +5896,10 @@ public class TelephonyManager { * {@link android.telephony.PhoneStateListener#onCellInfoChanged onCellInfoChanged()} * for each active subscription. * + * <p>This method returns valid data for devices with + * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. On devices + * that do not implement this feature, the behavior is not reliable. + * * @param workSource the requestor to whom the power consumption for this should be attributed. * @param executor the executor on which callback will be invoked. * @param callback a callback to receive CellInfo. diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java index 13bf17954aa4..2d2f4dbdf907 100644 --- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java +++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java @@ -107,7 +107,9 @@ public class AppLaunch extends InstrumentationTestCase { private static final int PROFILE_SAVE_SLEEP_TIMEOUT = 1000; // Allow 1s for the profile to save private static final int IORAP_TRACE_DURATION_TIMEOUT = 7000; // Allow 7s for trace to complete. private static final int IORAP_TRIAL_LAUNCH_ITERATIONS = 3; // min 3 launches to merge traces. - private static final int IORAP_COMPILE_CMD_TIMEOUT = 600; // in seconds: 10 minutes + private static final int IORAP_COMPILE_CMD_TIMEOUT = 60; // in seconds: 1 minutes + private static final int IORAP_COMPILE_MIN_TRACES = 1; // configure iorapd to need 1 trace. + private static final int IORAP_COMPILE_RETRIES = 3; // retry compiler 3 times if it fails. private static final String LAUNCH_SUB_DIRECTORY = "launch_logs"; private static final String LAUNCH_FILE = "applaunch.txt"; private static final String TRACE_SUB_DIRECTORY = "atrace_logs"; @@ -132,9 +134,9 @@ public class AppLaunch extends InstrumentationTestCase { private static final String LAUNCH_ORDER_CYCLIC = "cyclic"; private static final String LAUNCH_ORDER_SEQUENTIAL = "sequential"; private static final String COMPILE_CMD = "cmd package compile -f -m %s %s"; - private static final String IORAP_COMPILE_CMD = "cmd jobscheduler run -f android 283673059"; + private static final String IORAP_COMPILE_CMD = "dumpsys iorapd --compile-package %s"; private static final String IORAP_MAINTENANCE_CMD = - "iorap.cmd.maintenance --purge-package %s /data/misc/iorapd/sqlite.db"; + "dumpsys iorapd --purge-package %s"; private static final String IORAP_DUMPSYS_CMD = "dumpsys iorapd"; private static final String SPEED_PROFILE_FILTER = "speed-profile"; private static final String VERIFY_FILTER = "verify"; @@ -350,9 +352,9 @@ public class AppLaunch extends InstrumentationTestCase { sleep(IORAP_TRACE_DURATION_TIMEOUT); if (launch.getLaunchReason().equals(IORAP_TRIAL_LAUNCH_LAST)) { - // run the iorap job scheduler and wait for iorap to compile fully. - assertTrue(String.format("Not able to iorap-compile the app : %s", appPkgName), - compileAppForIorap(appPkgName)); + // run the iorap compiler and wait for iorap to compile fully. + // this throws an exception if it fails. + compileAppForIorapWithRetries(appPkgName, IORAP_COMPILE_RETRIES); } } @@ -506,6 +508,22 @@ public class AppLaunch extends InstrumentationTestCase { } /** + * Compile the app package using compilerFilter, + * retrying if the compilation command fails in between. + */ + private void compileAppForIorapWithRetries(String appPkgName, int retries) throws IOException { + for (int i = 0; i < retries; ++i) { + if (compileAppForIorap(appPkgName)) { + return; + } + sleep(1000); + } + + throw new IllegalStateException("compileAppForIorapWithRetries: timed out after " + + retries + " retries"); + } + + /** * Compile the app package using compilerFilter and return true or false * based on status of the compilation command. */ @@ -513,7 +531,7 @@ public class AppLaunch extends InstrumentationTestCase { String logcatTimestamp = getTimeNowForLogcat(); getInstrumentation().getUiAutomation(). - executeShellCommand(IORAP_COMPILE_CMD); + executeShellCommand(String.format(IORAP_COMPILE_CMD, appPkgName)); int i = 0; for (i = 0; i < IORAP_COMPILE_CMD_TIMEOUT; ++i) { @@ -525,7 +543,8 @@ public class AppLaunch extends InstrumentationTestCase { } else if (status == IorapCompilationStatus.INSUFFICIENT_TRACES) { Log.e(TAG, "compileAppForIorap: failed due to insufficient traces"); logDumpsysIorapd(appPkgName); - return false; + throw new IllegalStateException( + "compileAppForIorap: failed due to insufficient traces"); } // else INCOMPLETE. keep asking iorapd if it's done yet. sleep(1000); } @@ -536,19 +555,7 @@ public class AppLaunch extends InstrumentationTestCase { return false; } - // Wait for the job to finish completely. - // Other packages could be compiled in cyclic runs. - int currentAttempt = 0; - do { - String logcatLines = getLogcatSinceTime(logcatTimestamp); - if (logcatLines.contains("IorapForwardingService: Finished background job")) { - return true; - } - sleep(1000); - } while (currentAttempt++ < IORAP_COMPILE_CMD_TIMEOUT); - - Log.e(TAG, "compileAppForIorap: failed due to jobscheduler timeout."); - return false; + return true; } /** Save the contents of $(adb shell dumpsys iorapd) to the launch_logs directory. */ @@ -808,11 +815,9 @@ public class AppLaunch extends InstrumentationTestCase { } Log.v(TAG, "Purge iorap package: " + packageName); - stopIorapd(); getInstrumentation().getUiAutomation() .executeShellCommand(String.format(IORAP_MAINTENANCE_CMD, packageName)); Log.v(TAG, "Executed: " + String.format(IORAP_MAINTENANCE_CMD, packageName)); - startIorapd(); } String executeShellCommandWithTempFile(String cmd) { @@ -892,12 +897,16 @@ public class AppLaunch extends InstrumentationTestCase { throw new AssertionError(e); } - stopIorapd(); getInstrumentation().getUiAutomation() .executeShellCommand(String.format("setprop iorapd.perfetto.enable %b", enable)); getInstrumentation().getUiAutomation() .executeShellCommand(String.format("setprop iorapd.readahead.enable %b", enable)); - startIorapd(); + getInstrumentation().getUiAutomation() + .executeShellCommand(String.format( + "setprop iorapd.maintenance.min_traces %d", IORAP_COMPILE_MIN_TRACES)); + // this last command blocks until iorapd refreshes its system properties + getInstrumentation().getUiAutomation() + .executeShellCommand(String.format("dumpsys iorapd --refresh-properties")); if (enable) { mIorapStatus = IorapStatus.ENABLED; diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java index b40d022f075d..86c3fa0fe034 100644 --- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java @@ -53,8 +53,9 @@ public class TaskOrganizerMultiWindowTest extends Activity { return true; } - float x = e.getX(0); + float x = e.getRawX(0); float ratio = (float) x / (float) getWidth() ; + ratio = 1-ratio; LinearLayout.LayoutParams lp1 = new LinearLayout.LayoutParams(0, @@ -172,10 +173,14 @@ public class TaskOrganizerMultiWindowTest extends Activity { setContentView(splitView); } + private void addFlags(Intent intent) { + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION); + } + Intent makeSettingsIntent() { Intent intent = new Intent(); intent.setAction(android.provider.Settings.ACTION_SETTINGS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + addFlags(intent); return intent; } @@ -183,7 +188,7 @@ public class TaskOrganizerMultiWindowTest extends Activity { Intent intent = new Intent(); intent.setAction(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_APP_CONTACTS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + addFlags(intent); return intent; } diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java index 03615f332723..aa041f22a46e 100644 --- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java @@ -80,8 +80,10 @@ class TaskView extends SurfaceView implements SurfaceHolder.Callback { } catch (Exception e) { // System server died.. oh well } + t.reparent(leash, getSurfaceControl()) .setPosition(leash, 0, 0) + .show(leash) .apply(); } } diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt index cf11f4347503..f0333c98b8a7 100644 --- a/wifi/jarjar-rules.txt +++ b/wifi/jarjar-rules.txt @@ -10,11 +10,17 @@ rule android.net.NetworkFactory* com.android.server.x.wifi.net.NetworkFactory@1 rule android.net.ip.IpClientCallbacks* com.android.server.x.wifi.net.ip.IpClientCallbacks@1 rule android.net.ip.IpClientManager* com.android.server.x.wifi.net.ip.IpClientManager@1 rule android.net.ip.IpClientUtil* com.android.server.x.wifi.net.ip.IpClientUtil@1 +rule android.net.ipmemorystore.OnBlobRetrievedListener* com.android.server.x.wifi.net.ipmemorystore.OnBlobRetrievedListener@1 +rule android.net.ipmemorystore.OnStatusListener* com.android.server.x.wifi.net.ipmemorystore.OnStatusListener@1 +rule android.net.ipmemorystore.StatusParcelable* @0 +rule android.net.ipmemorystore.Status* com.android.server.x.wifi.net.ipmemorystore.Status@1 +rule android.net.networkstack.ModuleNetworkStackClient* com.android.server.x.wifi.net.networkstack.ModuleNetworkStackClient@1 +rule android.net.networkstack.NetworkStackClientBase* com.android.server.x.wifi.net.networkstack.NetworkStackClientBase@1 rule android.net.shared.InetAddressUtils* com.android.server.x.wifi.net.shared.InetAddressUtils@1 rule android.net.shared.InitialConfiguration* com.android.server.x.wifi.net.shared.InitialConfiguration@1 rule android.net.shared.IpConfigurationParcelableUtil* com.android.server.x.wifi.net.shared.IpConfigurationParcelableUtil@1 +rule android.net.shared.Layer2Information* com.android.server.x.wifi.net.shared.Layer2Information@1 rule android.net.shared.LinkPropertiesParcelableUtil* com.android.server.x.wifi.net.shared.LinkPropertiesParcelableUtil@1 -rule android.net.shared.ParcelableUtil* com.android.server.x.wifi.net.shared.ParcelableUtil@1 rule android.net.shared.NetdUtils* com.android.server.x.wifi.net.shared.NetdUtils@1 rule android.net.shared.NetworkMonitorUtils* com.android.server.x.wifi.net.shared.NetworkMonitorUtils@1 rule android.net.shared.ParcelableUtil* com.android.server.x.wifi.net.shared.ParcelableUtil@1 @@ -28,6 +34,8 @@ rule android.net.util.SharedLog* com.android.server.x.wifi.net.util.SharedLog@1 rule android.net.util.NetUtils* com.android.server.x.wifi.net.util.NetUtils@1 rule android.net.util.IpUtils* com.android.server.x.wifi.net.util.IpUtils@1 +rule androidx.annotation.** com.android.server.x.wifi.androidx.annotation.@1 + # We don't jar-jar the entire package because, we still use some classes (like # AsyncChannel in com.android.internal.util) from these packages which are not # inside our jar (currently in framework.jar, but will be in wifisdk.jar in the future). @@ -56,6 +64,10 @@ rule android.sysprop.** com.android.server.x.wifi.sysprop.@1 # Use our statically linked HIDL stubs rule android.hardware.** com.android.server.x.wifi.hardware.@1 rule android.hidl.** com.android.server.x.wifi.hidl.@1 +# Use our statically linked ksoap2 +rule org.ksoap2.** com.android.server.x.wifi.ksoap2.@1 +# Use our statically linked nanohttpd +rule fi.iki.elonen.** com.android.server.x.wifi.elonen.@1 # used by both framework-wifi and wifi-service rule android.content.pm.BaseParceledListSlice* android.x.net.wifi.util.BaseParceledListSlice@1 diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index f1be8b20eb53..6c8dc00cb579 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -179,6 +179,8 @@ public class WifiManager { /** * Reason code if one or more of the network suggestions added already exists in platform's * database. + * Note: this code will not be returned with Android 11 as in-place modification is allowed, + * please check {@link #addNetworkSuggestions(List)}. * @see WifiNetworkSuggestion#equals(Object) */ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 3; @@ -186,6 +188,8 @@ public class WifiManager { /** * Reason code if the number of network suggestions provided by the app crosses the max * threshold set per app. + * The framework will reject all suggestions provided by {@link #addNetworkSuggestions(List)} if + * the total size exceeds the limit. * @see #getMaxNumberOfNetworkSuggestionsPerApp() */ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 4; @@ -193,21 +197,27 @@ public class WifiManager { /** * Reason code if one or more of the network suggestions removed does not exist in platform's * database. + * The framework won't remove any suggestions if one or more of suggestions provided + * by {@link #removeNetworkSuggestions(List)} does not exist in database. + * @see WifiNetworkSuggestion#equals(Object) */ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 5; /** * Reason code if one or more of the network suggestions added is not allowed. - * + * The framework will reject all suggestions provided by {@link #addNetworkSuggestions(List)} + * if one or more of them is not allowed. * This error may be caused by suggestion is using SIM-based encryption method, but calling app * is not carrier privileged. */ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_NOT_ALLOWED = 6; /** - * Reason code if one or more of the network suggestions added is invalid. - * - * Please user {@link WifiNetworkSuggestion.Builder} to create network suggestions. + * Reason code if one or more of the network suggestions added is invalid. Framework will reject + * all the suggestions in the list. + * The framework will reject all suggestions provided by {@link #addNetworkSuggestions(List)} + * if one or more of them is invalid. + * Please use {@link WifiNetworkSuggestion.Builder} to create network suggestions. */ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_INVALID = 7; @@ -1887,7 +1897,7 @@ public class WifiManager { * <li> If user reset network settings, all added suggestions will be discarded. Apps can use * {@link #getNetworkSuggestions()} to check if their suggestions are in the device.</li> * <li> In-place modification of existing suggestions are allowed. - * <li>If the provided suggestions includes any previously provided suggestions by the app, + * <li> If the provided suggestions include any previously provided suggestions by the app, * previous suggestions will be updated.</li> * <li>If one of the provided suggestions marks a previously unmetered suggestion as metered and * the device is currently connected to that suggested network, then the device will disconnect |