summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt2
-rw-r--r--cmds/statsd/src/atoms.proto57
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp3
-rw-r--r--core/java/android/app/JobSchedulerImpl.java4
-rw-r--r--core/java/android/app/Notification.java4
-rw-r--r--core/java/android/app/VoiceInteractor.java10
-rw-r--r--core/java/android/app/job/IJobScheduler.aidl5
-rw-r--r--core/java/android/app/usage/NetworkStatsManager.java20
-rw-r--r--core/java/android/content/ContentResolver.java15
-rw-r--r--core/java/android/content/Intent.java25
-rw-r--r--core/java/android/os/storage/StorageManager.java2
-rw-r--r--core/java/android/os/storage/VolumeRecord.java9
-rw-r--r--core/java/android/service/appprediction/AppPredictionService.java16
-rw-r--r--core/java/android/util/FeatureFlagUtils.java6
-rw-r--r--core/java/android/util/MemoryIntArray.java41
-rw-r--r--core/java/android/view/SurfaceControl.java3
-rw-r--r--core/java/android/view/WindowInsets.java4
-rw-r--r--core/java/com/android/internal/content/FileSystemProvider.java12
-rw-r--r--core/java/com/android/internal/infra/AbstractRemoteService.java6
-rw-r--r--core/jni/android_util_MemoryIntArray.cpp2
-rw-r--r--core/res/AndroidManifest.xml9
-rw-r--r--core/res/res/layout/media_route_chooser_dialog.xml2
-rw-r--r--core/res/res/layout/media_route_list_item.xml2
-rw-r--r--core/res/res/values/config.xml9
-rw-r--r--core/res/res/values/strings.xml7
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/bugreports/Android.bp27
-rw-r--r--core/tests/bugreports/AndroidManifest.xml24
-rw-r--r--core/tests/bugreports/AndroidTest.xml35
-rw-r--r--core/tests/bugreports/config/test-sysconfig.xml20
-rwxr-xr-xcore/tests/bugreports/run.sh61
-rw-r--r--core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java341
-rw-r--r--core/tests/utiltests/Android.mk1
-rw-r--r--core/tests/utiltests/jni/android_util_MemoryIntArrayTest.cpp30
-rw-r--r--core/tests/utiltests/jni/registration.cpp11
-rw-r--r--core/tests/utiltests/src/android/util/MemoryIntArrayTest.java20
-rw-r--r--data/etc/com.android.dialer.xml2
-rw-r--r--graphics/java/android/graphics/drawable/GradientDrawable.java170
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp2
-rw-r--r--libs/hwui/renderthread/VulkanManager.h7
-rw-r--r--libs/hwui/renderthread/VulkanSurface.cpp5
-rw-r--r--media/java/android/media/ThumbnailUtils.java9
-rw-r--r--packages/CaptivePortalLogin/Android.bp20
-rw-r--r--packages/CarSystemUI/AndroidManifest.xml4
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java55
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java93
-rw-r--r--packages/NetworkPermissionConfig/Android.bp21
-rw-r--r--packages/NetworkStack/Android.bp9
-rw-r--r--packages/NetworkStack/AndroidManifest.xml2
-rw-r--r--packages/NetworkStack/src/android/net/ip/IpClient.java13
-rw-r--r--packages/NetworkStack/src/com/android/networkstack/util/DnsUtils.java23
-rw-r--r--packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java69
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java14
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java26
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java32
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java3
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java20
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java49
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java11
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java42
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java2
-rw-r--r--packages/SettingsProvider/res/values/defaults.xml2
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java15
-rw-r--r--packages/SystemUI/Android.bp1
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/LegacyRecentsImpl.java6
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml1
-rw-r--r--packages/SystemUI/res/drawable/bubble_flyout.xml30
-rw-r--r--packages/SystemUI/res/layout/biometric_dialog.xml3
-rw-r--r--packages/SystemUI/res/layout/bubble_flyout.xml13
-rw-r--r--packages/SystemUI/res/layout/keyguard_bottom_area.xml41
-rw-r--r--packages/SystemUI/res/layout/left_docked_overlay.xml19
-rw-r--r--packages/SystemUI/res/layout/right_docked_overlay.xml19
-rw-r--r--packages/SystemUI/res/layout/super_status_bar.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml12
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java20
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BadgeRenderer.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java412
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java347
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleFlyoutViewTest.java92
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java62
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java1
-rw-r--r--packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml4
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java3
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java16
-rw-r--r--services/core/java/com/android/server/Watchdog.java79
-rw-r--r--services/core/java/com/android/server/am/PreBootBroadcaster.java8
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java16
-rw-r--r--services/core/java/com/android/server/attention/AttentionManagerService.java2
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java5
-rw-r--r--services/core/java/com/android/server/connectivity/KeepaliveTracker.java77
-rw-r--r--services/core/java/com/android/server/connectivity/PermissionMonitor.java4
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java17
-rw-r--r--services/core/java/com/android/server/display/color/ColorDisplayService.java8
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java32
-rw-r--r--services/core/java/com/android/server/location/GnssConfiguration.java6
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java73
-rw-r--r--services/core/java/com/android/server/location/GnssVisibilityControl.java26
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java12
-rw-r--r--services/core/java/com/android/server/pm/Installer.java1
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java28
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java58
-rw-r--r--services/core/java/com/android/server/power/AttentionDetector.java8
-rw-r--r--services/core/java/com/android/server/stats/StatsCompanionService.java60
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java94
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartInterceptor.java3
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java12
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java34
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java8
-rw-r--r--services/core/java/com/android/server/wm/Task.java1
-rw-r--r--services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java13
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java37
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java8
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp9
-rw-r--r--services/java/com/android/server/SystemServer.java10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java183
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java55
-rw-r--r--services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java15
-rw-r--r--services/usage/java/com/android/server/usage/AppStandbyController.java2
-rw-r--r--services/usage/java/com/android/server/usage/StorageStatsService.java1
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java5
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java5
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java16
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java38
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java15
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java20
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java17
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl11
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java8
-rw-r--r--tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java8
-rw-r--r--wifi/java/android/net/wifi/ScanResult.java5
158 files changed, 3122 insertions, 1091 deletions
diff --git a/api/current.txt b/api/current.txt
index 8b24826a267b..54fb459b3bc8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -143,6 +143,7 @@ package android {
field public static final String SET_WALLPAPER_HINTS = "android.permission.SET_WALLPAPER_HINTS";
field public static final String SIGNAL_PERSISTENT_PROCESSES = "android.permission.SIGNAL_PERSISTENT_PROCESSES";
field public static final String SMS_FINANCIAL_TRANSACTIONS = "android.permission.SMS_FINANCIAL_TRANSACTIONS";
+ field public static final String START_VIEW_PERMISSION_USAGE = "android.permission.START_VIEW_PERMISSION_USAGE";
field public static final String STATUS_BAR = "android.permission.STATUS_BAR";
field public static final String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW";
field public static final String TRANSMIT_IR = "android.permission.TRANSMIT_IR";
@@ -10326,6 +10327,7 @@ package android.content {
field public static final String ACTION_USER_UNLOCKED = "android.intent.action.USER_UNLOCKED";
field public static final String ACTION_VIEW = "android.intent.action.VIEW";
field public static final String ACTION_VIEW_LOCUS = "android.intent.action.VIEW_LOCUS";
+ field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE = "android.intent.action.VIEW_PERMISSION_USAGE";
field public static final String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
field @Deprecated public static final String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED";
field public static final String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH";
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index b9a4b52d6c48..abcccf89c2a8 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -298,6 +298,7 @@ message Atom {
VehicleMapServicePacketFailureReported vms_packet_failure_reported = 202;
CarPowerStateChanged car_power_state_changed = 203;
GarageModeInfo garage_mode_info = 204;
+ TestAtomReported test_atom_reported = 205 [(log_from_module) = "cts"];
}
// Pulled events will start at field 10000.
@@ -363,6 +364,7 @@ message Atom {
AppsOnExternalStorageInfo apps_on_external_storage_info = 10057;
FaceSettings face_settings = 10058;
CoolingDevice cooling_device = 10059;
+ AppOps app_ops = 10060;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -3342,6 +3344,23 @@ message BinaryPushStateChanged {
optional int32 user_id = 8;
}
+/* Test atom, is not logged anywhere */
+message TestAtomReported {
+ repeated AttributionNode attribution_node = 1;
+ optional int32 int_field = 2;
+ optional int64 long_field = 3;
+ optional float float_field = 4;
+ optional string string_field = 5;
+ optional bool boolean_field = 6;
+ enum State {
+ UNKNOWN = 0;
+ OFF = 1;
+ ON = 2;
+ }
+ optional State state = 7;
+ optional TrainExperimentIds bytes_field = 8 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
/** Represents USB port overheat event. */
message UsbPortOverheatEvent {
/* Temperature of USB port at USB plug event, in 1/10ths of degree C. */
@@ -6339,3 +6358,41 @@ message GarageModeInfo {
// Whether GarageMode is entered.
optional bool is_garage_mode = 1;
}
+
+/**
+ * Historical app ops data per package.
+ */
+message AppOps {
+ // Uid of the package requesting the op
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Nmae of the package performing the op
+ optional string package_name = 2;
+
+ // operation id; maps to the OP_* constants in AppOpsManager.java
+ optional int32 op_id = 3;
+
+ // The number of times the op was granted while the app was in the
+ // foreground (only for trusted requests)
+ optional int64 trusted_foreground_granted_count = 4;
+
+ // The number of times the op was granted while the app was in the
+ // background (only for trusted requests)
+ optional int64 trusted_background_granted_count = 5;
+
+ // The number of times the op was rejected while the app was in the
+ // foreground (only for trusted requests)
+ optional int64 trusted_foreground_rejected_count = 6;
+
+ // The number of times the op was rejected while the app was in the
+ // background (only for trusted requests)
+ optional int64 trusted_background_rejected_count = 7;
+
+ // For long-running operations, total duration of the operation
+ // while the app was in the foreground (only for trusted requests)
+ optional int64 trusted_foreground_duration_millis = 8;
+
+ // For long-running operations, total duration of the operation
+ // while the app was in the background (only for trusted requests)
+ optional int64 trusted_background_duration_millis = 9;
+}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 2ffe18e20c24..914d60d3daca 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -260,6 +260,9 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
// Face Settings
{android::util::FACE_SETTINGS,
{.puller = new StatsCompanionServicePuller(android::util::FACE_SETTINGS)}},
+ // App ops
+ {android::util::APP_OPS,
+ {.puller = new StatsCompanionServicePuller(android::util::APP_OPS)}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/core/java/android/app/JobSchedulerImpl.java b/core/java/android/app/JobSchedulerImpl.java
index 5494e2a8855c..e8770185305c 100644
--- a/core/java/android/app/JobSchedulerImpl.java
+++ b/core/java/android/app/JobSchedulerImpl.java
@@ -83,7 +83,7 @@ public class JobSchedulerImpl extends JobScheduler {
@Override
public List<JobInfo> getAllPendingJobs() {
try {
- return mBinder.getAllPendingJobs();
+ return mBinder.getAllPendingJobs().getList();
} catch (RemoteException e) {
return null;
}
@@ -110,7 +110,7 @@ public class JobSchedulerImpl extends JobScheduler {
@Override
public List<JobSnapshot> getAllJobSnapshots() {
try {
- return mBinder.getAllJobSnapshots();
+ return mBinder.getAllJobSnapshots().getList();
} catch (RemoteException e) {
return null;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b4c6d94cc823..789351e0d157 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -8832,8 +8832,8 @@ public class Notification implements Parcelable
* <p>Setting this flag is optional; it defaults to false.</p>
*/
@NonNull
- public BubbleMetadata.Builder setSuppressNotification(boolean shouldSupressNotif) {
- setFlag(FLAG_SUPPRESS_NOTIFICATION, shouldSupressNotif);
+ public BubbleMetadata.Builder setSuppressNotification(boolean shouldSuppressNotif) {
+ setFlag(FLAG_SUPPRESS_NOTIFICATION, shouldSuppressNotif);
return this;
}
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index 7828573885a3..b37120faf281 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -79,10 +79,10 @@ public final class VoiceInteractor {
/** @hide */
public static final String KEY_KILL_SIGNAL = "key_kill_signal";
- IVoiceInteractor mInteractor;
+ @Nullable IVoiceInteractor mInteractor;
- Context mContext;
- Activity mActivity;
+ @Nullable Context mContext;
+ @Nullable Activity mActivity;
boolean mRetaining;
final HandlerCaller mHandlerCaller;
@@ -999,7 +999,9 @@ public final class VoiceInteractor {
// destroyed now
mInteractor = null;
- mActivity.setVoiceInteractor(null);
+ if (mActivity != null) {
+ mActivity.setVoiceInteractor(null);
+ }
}
public boolean submitRequest(Request request) {
diff --git a/core/java/android/app/job/IJobScheduler.aidl b/core/java/android/app/job/IJobScheduler.aidl
index 53b33c22dd81..3006f50e54fc 100644
--- a/core/java/android/app/job/IJobScheduler.aidl
+++ b/core/java/android/app/job/IJobScheduler.aidl
@@ -19,6 +19,7 @@ package android.app.job;
import android.app.job.JobInfo;
import android.app.job.JobSnapshot;
import android.app.job.JobWorkItem;
+import android.content.pm.ParceledListSlice;
/**
* IPC interface that supports the app-facing {@link #JobScheduler} api.
@@ -30,8 +31,8 @@ interface IJobScheduler {
int scheduleAsPackage(in JobInfo job, String packageName, int userId, String tag);
void cancel(int jobId);
void cancelAll();
- List<JobInfo> getAllPendingJobs();
+ ParceledListSlice getAllPendingJobs();
JobInfo getPendingJob(int jobId);
List<JobInfo> getStartedJobs();
- List<JobSnapshot> getAllJobSnapshots();
+ ParceledListSlice getAllJobSnapshots();
}
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 59ae3347f417..8e40449fa546 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -278,6 +278,12 @@ public class NetworkStatsManager {
return null;
}
+ return querySummary(template, startTime, endTime);
+ }
+
+ /** @hide */
+ public NetworkStats querySummary(NetworkTemplate template, long startTime,
+ long endTime) throws SecurityException, RemoteException {
NetworkStats result;
result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
result.startSummaryEnumeration();
@@ -296,6 +302,13 @@ public class NetworkStatsManager {
NetworkStats.Bucket.TAG_NONE, NetworkStats.Bucket.STATE_ALL);
}
+ /** @hide */
+ public NetworkStats queryDetailsForUid(NetworkTemplate template,
+ long startTime, long endTime, int uid) throws SecurityException {
+ return queryDetailsForUidTagState(template, startTime, endTime, uid,
+ NetworkStats.Bucket.TAG_NONE, NetworkStats.Bucket.STATE_ALL);
+ }
+
/**
* Query network usage statistics details for a given uid and tag.
*
@@ -340,6 +353,13 @@ public class NetworkStatsManager {
NetworkTemplate template;
template = createTemplate(networkType, subscriberId);
+ return queryDetailsForUidTagState(template, startTime, endTime, uid, tag, state);
+ }
+
+ /** @hide */
+ public NetworkStats queryDetailsForUidTagState(NetworkTemplate template,
+ long startTime, long endTime, int uid, int tag, int state) throws SecurityException {
+
NetworkStats result;
try {
result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 804677648d09..2c5860ac8775 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -825,7 +825,9 @@ public abstract class ContentResolver implements ContentInterface {
* @param sortOrder How to order the rows, formatted as an SQL ORDER BY
* clause (excluding the ORDER BY itself). Passing null will use the
* default sort order, which may be unordered.
- * @return A Cursor object, which is positioned before the first entry, or null
+ * @return A Cursor object, which is positioned before the first entry. May return
+ * <code>null</code> if the underlying content provider returns <code>null</code>,
+ * or if it crashes.
* @see Cursor
*/
public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
@@ -865,7 +867,9 @@ public abstract class ContentResolver implements ContentInterface {
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* If the operation is canceled, then {@link OperationCanceledException} will be thrown
* when the query is executed.
- * @return A Cursor object, which is positioned before the first entry, or null
+ * @return A Cursor object, which is positioned before the first entry. May return
+ * <code>null</code> if the underlying content provider returns <code>null</code>,
+ * or if it crashes.
* @see Cursor
*/
public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
@@ -902,7 +906,9 @@ public abstract class ContentResolver implements ContentInterface {
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* If the operation is canceled, then {@link OperationCanceledException} will be thrown
* when the query is executed.
- * @return A Cursor object, which is positioned before the first entry, or null
+ * @return A Cursor object, which is positioned before the first entry. May return
+ * <code>null</code> if the underlying content provider returns <code>null</code>,
+ * or if it crashes.
* @see Cursor
*/
@Override
@@ -1799,7 +1805,8 @@ public abstract class ContentResolver implements ContentInterface {
* @param url The URL of the table to insert into.
* @param values The initial values for the newly inserted row. The key is the column name for
* the field. Passing an empty ContentValues will create an empty row.
- * @return the URL of the newly created row.
+ * @return the URL of the newly created row. May return <code>null</code> if the underlying
+ * content provider returns <code>null</code>, or if it crashes.
*/
@Override
public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 50d1785c6059..9e5fcfb6f73e 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1881,6 +1881,31 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.REVIEW_PERMISSIONS";
/**
+ * Activity action: Launch UI to show information about the usage
+ * of a given permission. This action would be handled by apps that
+ * want to show details about how and why given permission is being
+ * used.
+ * <p>
+ * <strong>Important:</strong>You must protect the activity that handles
+ * this action with the {@link android.Manifest.permission#START_VIEW_PERMISSION_USAGE
+ * START_VIEW_PERMISSION_USAGE} permission to ensure that only the
+ * system can launch this activity. The system will not launch
+ * activities that are not properly protected.
+ *
+ * <p>
+ * Input: {@code android.intent.extra.PERMISSION_NAME} specifies the permission
+ * for which the launched UI would be targeted.
+ * </p>
+ * <p>
+ * Output: Nothing.
+ * </p>
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE)
+ public static final String ACTION_VIEW_PERMISSION_USAGE =
+ "android.intent.action.VIEW_PERMISSION_USAGE";
+
+ /**
* Activity action: Launch UI to manage a default app.
* <p>
* Input: {@link #EXTRA_ROLE_NAME} specifies the role of the default app which will be managed
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index ee62af57b9a0..69c1295df4f9 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -248,6 +248,8 @@ public class StorageManager {
public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE;
/** {@hide} */
public static final int FLAG_STORAGE_CE = IInstalld.FLAG_STORAGE_CE;
+ /** {@hide} */
+ public static final int FLAG_STORAGE_EXTERNAL = IInstalld.FLAG_STORAGE_EXTERNAL;
/** {@hide} */
public static final int FLAG_FOR_WRITE = 1 << 8;
diff --git a/core/java/android/os/storage/VolumeRecord.java b/core/java/android/os/storage/VolumeRecord.java
index b6ee26114963..1a794ebf2a59 100644
--- a/core/java/android/os/storage/VolumeRecord.java
+++ b/core/java/android/os/storage/VolumeRecord.java
@@ -25,6 +25,7 @@ import android.util.TimeUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import java.util.Locale;
import java.util.Objects;
/**
@@ -45,6 +46,7 @@ public class VolumeRecord implements Parcelable {
public String nickname;
public int userFlags;
public long createdMillis;
+ public long lastSeenMillis;
public long lastTrimMillis;
public long lastBenchMillis;
@@ -61,6 +63,7 @@ public class VolumeRecord implements Parcelable {
nickname = parcel.readString();
userFlags = parcel.readInt();
createdMillis = parcel.readLong();
+ lastSeenMillis = parcel.readLong();
lastTrimMillis = parcel.readLong();
lastBenchMillis = parcel.readLong();
}
@@ -73,6 +76,10 @@ public class VolumeRecord implements Parcelable {
return fsUuid;
}
+ public String getNormalizedFsUuid() {
+ return fsUuid != null ? fsUuid.toLowerCase(Locale.US) : null;
+ }
+
public String getNickname() {
return nickname;
}
@@ -97,6 +104,7 @@ public class VolumeRecord implements Parcelable {
DebugUtils.flagsToString(VolumeRecord.class, "USER_FLAG_", userFlags));
pw.println();
pw.printPair("createdMillis", TimeUtils.formatForLogging(createdMillis));
+ pw.printPair("lastSeenMillis", TimeUtils.formatForLogging(lastSeenMillis));
pw.printPair("lastTrimMillis", TimeUtils.formatForLogging(lastTrimMillis));
pw.printPair("lastBenchMillis", TimeUtils.formatForLogging(lastBenchMillis));
pw.decreaseIndent();
@@ -155,6 +163,7 @@ public class VolumeRecord implements Parcelable {
parcel.writeString(nickname);
parcel.writeInt(userFlags);
parcel.writeLong(createdMillis);
+ parcel.writeLong(lastSeenMillis);
parcel.writeLong(lastTrimMillis);
parcel.writeLong(lastBenchMillis);
}
diff --git a/core/java/android/service/appprediction/AppPredictionService.java b/core/java/android/service/appprediction/AppPredictionService.java
index 1391d43b00ca..be20570ef62d 100644
--- a/core/java/android/service/appprediction/AppPredictionService.java
+++ b/core/java/android/service/appprediction/AppPredictionService.java
@@ -39,6 +39,7 @@ import android.os.Looper;
import android.os.RemoteException;
import android.service.appprediction.IPredictionService.Stub;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Slog;
import java.util.ArrayList;
@@ -46,7 +47,7 @@ import java.util.List;
import java.util.function.Consumer;
/**
- * TODO(b/111701043): Add java docs
+ * A service used to predict app and shortcut usage.
*
* @hide
*/
@@ -58,7 +59,9 @@ public abstract class AppPredictionService extends Service {
/**
* The {@link Intent} that must be declared as handled by the service.
- * TODO(b/111701043): Add any docs about permissions the service must hold
+ *
+ * <p>The service must also require the {@link android.permission#MANAGE_APP_PREDICTIONS}
+ * permission.
*
* @hide
*/
@@ -145,8 +148,11 @@ public abstract class AppPredictionService extends Service {
@Override
@NonNull
public final IBinder onBind(@NonNull Intent intent) {
- // TODO(b/111701043): Verify that the action is valid
- return mInterface.asBinder();
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mInterface.asBinder();
+ }
+ Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
+ return null;
}
/**
@@ -180,7 +186,6 @@ public abstract class AppPredictionService extends Service {
/**
* Called by the client app to request sorting of targets based on prediction rank.
- * TODO(b/111701043): Implement CancellationSignal so caller can cancel a long running request
*/
@MainThread
public abstract void onSortAppTargets(@NonNull AppPredictionSessionId sessionId,
@@ -254,7 +259,6 @@ public abstract class AppPredictionService extends Service {
/**
* Called by the client app to request target predictions. This method is only called if there
* are one or more prediction callbacks registered.
- * TODO(b/111701043): Add java docs
*
* @see #updatePredictions(AppPredictionSessionId, List)
*/
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index ae36e4ecde17..c42dc817bec4 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -37,10 +37,6 @@ public class FeatureFlagUtils {
public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
- public static final String FORCE_GLOBAL_ACTIONS_GRID_ENABLED =
- "settings_global_actions_force_grid_enabled";
- public static final String GLOBAL_ACTIONS_PANEL_ENABLED =
- "settings_global_actions_panel_enabled";
public static final String PIXEL_WALLPAPER_CATEGORY_SWITCH =
"settings_pixel_wallpaper_category_switch";
public static final String DYNAMIC_SYSTEM = "settings_dynamic_system";
@@ -57,8 +53,6 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
- DEFAULT_FLAGS.put(FORCE_GLOBAL_ACTIONS_GRID_ENABLED, "false");
- DEFAULT_FLAGS.put(GLOBAL_ACTIONS_PANEL_ENABLED, "true");
DEFAULT_FLAGS.put(PIXEL_WALLPAPER_CATEGORY_SWITCH, "false");
DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false");
}
diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java
index 74fea3f4dd30..80b16075cdf6 100644
--- a/core/java/android/util/MemoryIntArray.java
+++ b/core/java/android/util/MemoryIntArray.java
@@ -20,9 +20,8 @@ import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
-import dalvik.system.CloseGuard;
-
import libcore.io.IoUtils;
+import dalvik.system.CloseGuard;
import java.io.Closeable;
import java.io.IOException;
@@ -57,7 +56,7 @@ public final class MemoryIntArray implements Parcelable, Closeable {
private final boolean mIsOwner;
private final long mMemoryAddr;
- private ParcelFileDescriptor mFd;
+ private int mFd = -1;
/**
* Creates a new instance.
@@ -72,8 +71,8 @@ public final class MemoryIntArray implements Parcelable, Closeable {
}
mIsOwner = true;
final String name = UUID.randomUUID().toString();
- mFd = ParcelFileDescriptor.adoptFd(nativeCreate(name, size));
- mMemoryAddr = nativeOpen(mFd.getFd(), mIsOwner);
+ mFd = nativeCreate(name, size);
+ mMemoryAddr = nativeOpen(mFd, mIsOwner);
mCloseGuard.open("close");
}
@@ -83,8 +82,8 @@ public final class MemoryIntArray implements Parcelable, Closeable {
if (pfd == null) {
throw new IOException("No backing file descriptor");
}
- mFd = ParcelFileDescriptor.adoptFd(pfd.detachFd());
- mMemoryAddr = nativeOpen(mFd.getFd(), mIsOwner);
+ mFd = pfd.detachFd();
+ mMemoryAddr = nativeOpen(mFd, mIsOwner);
mCloseGuard.open("close");
}
@@ -106,7 +105,7 @@ public final class MemoryIntArray implements Parcelable, Closeable {
public int get(int index) throws IOException {
enforceNotClosed();
enforceValidIndex(index);
- return nativeGet(mFd.getFd(), mMemoryAddr, index);
+ return nativeGet(mFd, mMemoryAddr, index);
}
/**
@@ -122,7 +121,7 @@ public final class MemoryIntArray implements Parcelable, Closeable {
enforceNotClosed();
enforceWritable();
enforceValidIndex(index);
- nativeSet(mFd.getFd(), mMemoryAddr, index, value);
+ nativeSet(mFd, mMemoryAddr, index, value);
}
/**
@@ -132,7 +131,7 @@ public final class MemoryIntArray implements Parcelable, Closeable {
*/
public int size() throws IOException {
enforceNotClosed();
- return nativeSize(mFd.getFd());
+ return nativeSize(mFd);
}
/**
@@ -143,9 +142,8 @@ public final class MemoryIntArray implements Parcelable, Closeable {
@Override
public void close() throws IOException {
if (!isClosed()) {
- nativeClose(mFd.getFd(), mMemoryAddr, mIsOwner);
- mFd.close();
- mFd = null;
+ nativeClose(mFd, mMemoryAddr, mIsOwner);
+ mFd = -1;
mCloseGuard.close();
}
}
@@ -154,7 +152,7 @@ public final class MemoryIntArray implements Parcelable, Closeable {
* @return Whether this array is closed and shouldn't be used.
*/
public boolean isClosed() {
- return mFd == null;
+ return mFd == -1;
}
@Override
@@ -177,8 +175,13 @@ public final class MemoryIntArray implements Parcelable, Closeable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
- // Don't let writing to a parcel to close our fd - plz
- parcel.writeParcelable(mFd, flags & ~Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ ParcelFileDescriptor pfd = ParcelFileDescriptor.adoptFd(mFd);
+ try {
+ // Don't let writing to a parcel to close our fd - plz
+ parcel.writeParcelable(pfd, flags & ~Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ } finally {
+ pfd.detachFd();
+ }
}
@Override
@@ -192,13 +195,13 @@ public final class MemoryIntArray implements Parcelable, Closeable {
if (getClass() != obj.getClass()) {
return false;
}
-
- return false;
+ MemoryIntArray other = (MemoryIntArray) obj;
+ return mFd == other.mFd;
}
@Override
public int hashCode() {
- return mFd.hashCode();
+ return mFd;
}
private void enforceNotClosed() {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index d67c8847f3bc..63e14853b51d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -2660,6 +2660,9 @@ public final class SurfaceControl implements Parcelable {
*/
@NonNull
public Transaction merge(@NonNull Transaction other) {
+ if (this == other) {
+ return this;
+ }
mResizedSurfaces.putAll(other.mResizedSurfaces);
other.mResizedSurfaces.clear();
nativeMergeTransaction(mNativeObject, other.mNativeObject);
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 2d292ef7b25c..9340b71a5280 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -156,7 +156,9 @@ public final class WindowInsets {
* @param src Source to copy insets from
*/
public WindowInsets(WindowInsets src) {
- this(src.mTypeInsetsMap, src.mTypeMaxInsetsMap, src.mTypeVisibilityMap, src.mIsRound,
+ this(src.mSystemWindowInsetsConsumed ? null : src.mTypeInsetsMap,
+ src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap,
+ src.mTypeVisibilityMap, src.mIsRound,
src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src));
}
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index cc2caca49276..cdb79abbb7ce 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -247,7 +247,6 @@ public abstract class FileSystemProvider extends DocumentsProvider {
}
childId = getDocIdForFile(file);
onDocIdChanged(childId);
- addFolderToMediaStore(getFileForDocId(childId, true));
} else {
try {
if (!file.createNewFile()) {
@@ -259,19 +258,11 @@ public abstract class FileSystemProvider extends DocumentsProvider {
throw new IllegalStateException("Failed to touch " + file + ": " + e);
}
}
+ MediaStore.scanFile(getContext(), file);
return childId;
}
- private void addFolderToMediaStore(@Nullable File visibleFolder) {
- // visibleFolder is null if we're adding a folder to external thumb drive or SD card.
- if (visibleFolder != null) {
- assert (visibleFolder.isDirectory());
-
- MediaStore.scanFile(getContext(), visibleFolder);
- }
- }
-
@Override
public String renameDocument(String docId, String displayName) throws FileNotFoundException {
// Since this provider treats renames as generating a completely new
@@ -293,7 +284,6 @@ public abstract class FileSystemProvider extends DocumentsProvider {
moveInMediaStore(beforeVisibleFile, afterVisibleFile);
if (!TextUtils.equals(docId, afterDocId)) {
- scanFile(afterVisibleFile);
return afterDocId;
} else {
return null;
diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
index 64f885770336..3900f1674c13 100644
--- a/core/java/com/android/internal/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -231,6 +231,7 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I
@SuppressWarnings("unchecked") // TODO(b/117779333): fix this warning
final S castService = (S) this;
mVultureCallback.onServiceDied(castService);
+ handleBindFailure();
}
// Note: we are dumping without a lock held so this is a bit racy but
@@ -406,7 +407,8 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I
@NonNull BasePendingRequest<S, I> pendingRequest);
/**
- * Called if {@link Context#bindServiceAsUser} returns {@code false}.
+ * Called if {@link Context#bindServiceAsUser} returns {@code false}, or
+ * if {@link DeathRecipient#binderDied()} is called.
*/
abstract void handleBindFailure();
@@ -431,8 +433,6 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I
mBinding = false;
if (!mServiceDied) {
- // TODO(b/126266412): merge these 2 calls?
- handleBindFailure();
handleBinderDied();
}
}
diff --git a/core/jni/android_util_MemoryIntArray.cpp b/core/jni/android_util_MemoryIntArray.cpp
index b68f9eca70cd..2dfbe3ecfef6 100644
--- a/core/jni/android_util_MemoryIntArray.cpp
+++ b/core/jni/android_util_MemoryIntArray.cpp
@@ -142,6 +142,8 @@ static void android_util_MemoryIntArray_close(JNIEnv* env, jobject clazz, jint f
jniThrowException(env, "java/io/IOException", "ashmem unpinning failed");
return;
}
+
+ close(fd);
}
static jint android_util_MemoryIntArray_get(JNIEnv* env, jobject clazz,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 57b770455817..b634bb2a6e9e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4213,6 +4213,15 @@
android:description="@string/permdesc_bindCarrierServices"
android:protectionLevel="signature|privileged" />
+ <!--
+ Allows the holder to start the permission usage screen for an app.
+ <p>Protection level: signature|installer
+ -->
+ <permission android:name="android.permission.START_VIEW_PERMISSION_USAGE"
+ android:label="@string/permlab_startViewPermissionUsage"
+ android:description="@string/permdesc_startViewPermissionUsage"
+ android:protectionLevel="signature|installer" />
+
<!-- Allows an application to query whether DO_NOT_ASK_CREDENTIALS_ON_BOOT
flag is set.
@hide -->
diff --git a/core/res/res/layout/media_route_chooser_dialog.xml b/core/res/res/layout/media_route_chooser_dialog.xml
index d1c6267ea4e7..cd1c74fd7d7d 100644
--- a/core/res/res/layout/media_route_chooser_dialog.xml
+++ b/core/res/res/layout/media_route_chooser_dialog.xml
@@ -40,7 +40,7 @@
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:paddingLeft="16dp"
+ android:paddingStart="16dp"
android:text="@string/media_route_chooser_searching" />
</LinearLayout>
diff --git a/core/res/res/layout/media_route_list_item.xml b/core/res/res/layout/media_route_list_item.xml
index bdca433c1c58..e8460db69582 100644
--- a/core/res/res/layout/media_route_list_item.xml
+++ b/core/res/res/layout/media_route_list_item.xml
@@ -34,6 +34,7 @@
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="marquee"
+ android:textAlignment="viewStart"
android:textAppearance="?android:attr/textAppearanceMedium"
android:duplicateParentState="true" />
@@ -42,6 +43,7 @@
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="marquee"
+ android:textAlignment="viewStart"
android:textAppearance="?android:attr/textAppearanceSmall"
android:duplicateParentState="true" />
</LinearLayout>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3ac0af7b53a3..0daebd76daf4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1110,6 +1110,11 @@
-->
</integer-array>
+ <!-- Color mode to use when accessibility transforms are enabled. This color mode must be
+ supported by the device, but not necessarily appear in config_availableColorModes. The
+ regularly selected color mode will be used if this value is negative. -->
+ <integer name="config_accessibilityColorMode">-1</integer>
+
<!-- Indicate whether to allow the device to suspend when the screen is off
due to the proximity sensor. This resource should only be set to true
if the sensor HAL correctly handles the proximity sensor as a wake-up source.
@@ -3297,6 +3302,10 @@
{@link Window#setEnsuringNavigationBarContrastWhenTransparent}. -->
<bool name="config_navBarNeedsScrim">true</bool>
+ <!-- Controls whether seamless rotation should be allowed even though the navbar can move
+ (which normally prevents seamless rotation). -->
+ <bool name="config_allowSeamlessRotationDespiteNavBarMoving">false</bool>
+
<!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows.
These values are in DPs and will be converted to pixel sizes internally. -->
<string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">16x16</string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 5652c85cf28f..7de6ca5c0708 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1533,7 +1533,7 @@
<!-- Message shown during face acquisition when only the left part of the user's face was detected [CHAR LIMIT=50] -->
<string name="face_acquired_too_left">Move phone to the right.</string>
<!-- Message shown during face acquisition when the user is not front facing the sensor [CHAR LIMIT=50] -->
- <string name="face_acquired_poor_gaze">Look at the screen with your eyes open.</string>
+ <string name="face_acquired_poor_gaze">Please look more directly at your device.</string>
<!-- Message shown during face acquisition when the user is not detected [CHAR LIMIT=50] -->
<string name="face_acquired_not_detected">Can\u2019t see your face. Look at the phone.</string>
<!-- Message shown during face acquisition when the device is not steady [CHAR LIMIT=50] -->
@@ -1726,6 +1726,11 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_access_notification_policy">Allows the app to read and write Do Not Disturb configuration.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_startViewPermissionUsage">start view permission usage</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_startViewPermissionUsage">Allows the holder to start the permission usage for an app. Should never be needed for normal apps.</string>
+
<!-- Policy administration -->
<!-- Title of policy access to limiting the user's password choices -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 77988de771d9..1ef2eb4a0435 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2875,6 +2875,7 @@
<java-symbol type="bool" name="config_navBarTapThrough" />
<java-symbol type="bool" name="config_navBarAlwaysShowOnSideEdgeGesture" />
<java-symbol type="bool" name="config_navBarNeedsScrim" />
+ <java-symbol type="bool" name="config_allowSeamlessRotationDespiteNavBarMoving" />
<java-symbol type="dimen" name="config_backGestureInset" />
<java-symbol type="color" name="system_bar_background_semi_transparent" />
@@ -3186,6 +3187,7 @@
<java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficients" />
<java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficientsNative" />
<java-symbol type="array" name="config_availableColorModes" />
+ <java-symbol type="integer" name="config_accessibilityColorMode" />
<java-symbol type="bool" name="config_displayWhiteBalanceAvailable" />
<java-symbol type="bool" name="config_displayWhiteBalanceEnabledDefault" />
<java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureMin" />
diff --git a/core/tests/bugreports/Android.bp b/core/tests/bugreports/Android.bp
new file mode 100644
index 000000000000..d3bf0dd7a7e8
--- /dev/null
+++ b/core/tests/bugreports/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "BugreportManagerTestCases",
+ srcs: ["src/**/*.java"],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ static_libs: ["androidx.test.rules", "truth-prebuilt"],
+ test_suites: ["general-tests"],
+ sdk_version: "test_current",
+ platform_apis: true,
+}
+
diff --git a/core/tests/bugreports/AndroidManifest.xml b/core/tests/bugreports/AndroidManifest.xml
new file mode 100644
index 000000000000..0cfb8747e629
--- /dev/null
+++ b/core/tests/bugreports/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ android:installLocation="internalOnly"
+ package="com.android.os.bugreports.tests">
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.os.bugreports.tests"
+ android:label="Unit tests of BugreportManager" />
+</manifest>
diff --git a/core/tests/bugreports/AndroidTest.xml b/core/tests/bugreports/AndroidTest.xml
new file mode 100644
index 000000000000..410ca6043583
--- /dev/null
+++ b/core/tests/bugreports/AndroidTest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for BugreportManager test cases">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <option name="config-descriptor:metadata" key="component" value="framework"/>
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="BugreportManagerTestCases.apk"/>
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.os.bugreports.tests"/>
+ <!-- test-timeout unit is ms, value = 30 min -->
+ <option name="test-timeout" value="1800000" />
+ <option name="runtime-hint" value="30m" />
+ </test>
+</configuration>
diff --git a/core/tests/bugreports/config/test-sysconfig.xml b/core/tests/bugreports/config/test-sysconfig.xml
new file mode 100644
index 000000000000..09c69ba699de
--- /dev/null
+++ b/core/tests/bugreports/config/test-sysconfig.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- WARNING: This is a test config. -->
+<config>
+ <bugreport-whitelisted package="com.android.os.bugreports.tests" />
+</config>
diff --git a/core/tests/bugreports/run.sh b/core/tests/bugreports/run.sh
new file mode 100755
index 000000000000..010339836538
--- /dev/null
+++ b/core/tests/bugreports/run.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Script to run bugreport unitests
+# Must run on a rooted device.
+# Must run lunch before running the script
+# Usage: ${ANDROID_BUILD_TOP}/frameworks/base/core/tests/bugreports/run.sh
+
+# NOTE: This script replaces the framework-sysconfig.xml on your device, so use with caution.
+# It tries to replace it when done, but if the script does not finish cleanly
+# (for e.g. force stopped mid-way) your device will be left in an inconsistent state.
+# Reflashing will restore the right config.
+
+TMP_SYS_CONFIG=/var/tmp/framework-sysconfig.xml
+
+if [[ -z $ANDROID_PRODUCT_OUT ]]; then
+ echo "Please lunch before running this test."
+ exit 0
+fi
+
+# Print every command to console.
+set -x
+
+make -j BugreportManagerTestCases &&
+ adb root &&
+ adb remount &&
+ adb wait-for-device &&
+ # Save the sysconfig file in a tmp location and push the test config in
+ adb pull /system/etc/sysconfig/framework-sysconfig.xml "${TMP_SYS_CONFIG}" &&
+ adb push $ANDROID_BUILD_TOP/frameworks/base/core/tests/bugreports/config/test-sysconfig.xml /system/etc/sysconfig/framework-sysconfig.xml &&
+ # The test app needs to be a priv-app.
+ adb push $OUT/testcases/BugreportManagerTestCases/*/BugreportManagerTestCases.apk /system/priv-app ||
+ exit 1
+
+adb reboot &&
+adb wait-for-device &&
+atest BugreportManagerTest || echo "Tests FAILED!"
+
+# Restore the saved config file
+if [ -f "${TMP_SYS_CONFIG}" ]; then
+ SIZE=$(stat --printf="%s" "${TMP_SYS_CONFIG}")
+ if [ SIZE > 0 ]; then
+ adb remount &&
+ adb wait-for-device &&
+ adb push "${TMP_SYS_CONFIG}" /system/etc/sysconfig/framework-sysconfig.xml &&
+ rm "${TMP_SYS_CONFIG}"
+ fi
+fi
diff --git a/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java b/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java
new file mode 100644
index 000000000000..220f854c337a
--- /dev/null
+++ b/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.os.bugreports.tests;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.Manifest;
+import android.content.Context;
+import android.os.BugreportManager;
+import android.os.BugreportManager.BugreportCallback;
+import android.os.BugreportParams;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Tests for BugreportManager API.
+ */
+@RunWith(JUnit4.class)
+public class BugreportManagerTest {
+ @Rule public TestName name = new TestName();
+
+ private static final String TAG = "BugreportManagerTest";
+ private static final long BUGREPORT_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(10);
+
+ private Handler mHandler;
+ private Executor mExecutor;
+ private BugreportManager mBrm;
+ private ParcelFileDescriptor mBugreportFd;
+ private ParcelFileDescriptor mScreenshotFd;
+
+ @Before
+ public void setup() throws Exception {
+ mHandler = createHandler();
+ mExecutor = (runnable) -> {
+ if (mHandler != null) {
+ mHandler.post(() -> {
+ runnable.run();
+ });
+ }
+ };
+
+ mBrm = getBugreportManager();
+ mBugreportFd = parcelFd("bugreport_" + name.getMethodName(), ".zip");
+ mScreenshotFd = parcelFd("screenshot_" + name.getMethodName(), ".png");
+
+ getPermissions();
+ }
+
+ @After
+ public void teardown() throws Exception {
+ dropPermissions();
+ }
+
+
+ @Test
+ public void normalFlow_wifi() throws Exception {
+ BugreportCallbackImpl callback = new BugreportCallbackImpl();
+ mBrm.startBugreport(mBugreportFd, mScreenshotFd, wifi(), mExecutor, callback);
+ waitTillDoneOrTimeout(callback);
+
+ assertThat(callback.isDone()).isTrue();
+ // Wifi bugreports should not receive any progress.
+ assertThat(callback.hasReceivedProgress()).isFalse();
+ // TODO: Because of b/130234145, consent dialog is not shown; so we get a timeout error.
+ // When the bug is fixed, accept consent via UIAutomator and verify contents
+ // of mBugreportFd.
+ assertThat(callback.getErrorCode()).isEqualTo(
+ BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
+ assertFdsAreClosed(mBugreportFd, mScreenshotFd);
+ }
+
+ @Test
+ public void normalFlow_interactive() throws Exception {
+ BugreportCallbackImpl callback = new BugreportCallbackImpl();
+ mBrm.startBugreport(mBugreportFd, mScreenshotFd, interactive(), mExecutor, callback);
+
+ waitTillDoneOrTimeout(callback);
+ assertThat(callback.isDone()).isTrue();
+ // Interactive bugreports show progress updates.
+ assertThat(callback.hasReceivedProgress()).isTrue();
+ assertThat(callback.getErrorCode()).isEqualTo(
+ BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
+ assertFdsAreClosed(mBugreportFd, mScreenshotFd);
+ }
+
+ @Test
+ public void simultaneousBugreportsNotAllowed() throws Exception {
+ // Start bugreport #1
+ BugreportCallbackImpl callback = new BugreportCallbackImpl();
+ mBrm.startBugreport(mBugreportFd, mScreenshotFd, wifi(), mExecutor, callback);
+
+ // Before #1 is done, try to start #2.
+ assertThat(callback.isDone()).isFalse();
+ BugreportCallbackImpl callback2 = new BugreportCallbackImpl();
+ ParcelFileDescriptor bugreportFd2 = parcelFd("bugreport_2_" + name.getMethodName(), ".zip");
+ ParcelFileDescriptor screenshotFd2 =
+ parcelFd("screenshot_2_" + name.getMethodName(), ".png");
+ mBrm.startBugreport(bugreportFd2, screenshotFd2, wifi(), mExecutor, callback2);
+ Thread.sleep(500 /* .5s */);
+
+ // Verify #2 encounters an error.
+ assertThat(callback2.getErrorCode()).isEqualTo(
+ BugreportCallback.BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS);
+ assertFdsAreClosed(bugreportFd2, screenshotFd2);
+
+ // Cancel #1 so we can move on to the next test.
+ mBrm.cancelBugreport();
+ Thread.sleep(500 /* .5s */);
+ assertThat(callback.isDone()).isTrue();
+ assertFdsAreClosed(mBugreportFd, mScreenshotFd);
+ }
+
+ @Test
+ public void cancelBugreport() throws Exception {
+ // Start a bugreport.
+ BugreportCallbackImpl callback = new BugreportCallbackImpl();
+ mBrm.startBugreport(mBugreportFd, mScreenshotFd, wifi(), mExecutor, callback);
+
+ // Verify it's not finished yet.
+ assertThat(callback.isDone()).isFalse();
+
+ // Try to cancel it, but first without DUMP permission.
+ dropPermissions();
+ try {
+ mBrm.cancelBugreport();
+ fail("Expected cancelBugreport to throw SecurityException without DUMP permission");
+ } catch (SecurityException expected) {
+ }
+ assertThat(callback.isDone()).isFalse();
+
+ // Try again, with DUMP permission.
+ getPermissions();
+ mBrm.cancelBugreport();
+ Thread.sleep(500 /* .5s */);
+ assertThat(callback.isDone()).isTrue();
+ assertFdsAreClosed(mBugreportFd, mScreenshotFd);
+ }
+
+ @Test
+ public void insufficientPermissions_throwsException() throws Exception {
+ dropPermissions();
+
+ BugreportCallbackImpl callback = new BugreportCallbackImpl();
+ try {
+ mBrm.startBugreport(mBugreportFd, mScreenshotFd, wifi(), mExecutor, callback);
+ fail("Expected startBugreport to throw SecurityException without DUMP permission");
+ } catch (SecurityException expected) {
+ }
+ assertFdsAreClosed(mBugreportFd, mScreenshotFd);
+ }
+
+ @Test
+ public void invalidBugreportMode_throwsException() throws Exception {
+ BugreportCallbackImpl callback = new BugreportCallbackImpl();
+
+ try {
+ mBrm.startBugreport(mBugreportFd, mScreenshotFd,
+ new BugreportParams(25) /* unknown bugreport mode */, mExecutor, callback);
+ fail("Expected to throw IllegalArgumentException with unknown bugreport mode");
+ } catch (IllegalArgumentException expected) {
+ }
+ assertFdsAreClosed(mBugreportFd, mScreenshotFd);
+ }
+
+ private Handler createHandler() {
+ HandlerThread handlerThread = new HandlerThread("BugreportManagerTest");
+ handlerThread.start();
+ return new Handler(handlerThread.getLooper());
+ }
+
+ /* Implementatiion of {@link BugreportCallback} that offers wrappers around execution result */
+ private static final class BugreportCallbackImpl extends BugreportCallback {
+ private int mErrorCode = -1;
+ private boolean mSuccess = false;
+ private boolean mReceivedProgress = false;
+ private final Object mLock = new Object();
+
+ @Override
+ public void onProgress(float progress) {
+ synchronized (mLock) {
+ mReceivedProgress = true;
+ }
+ }
+
+ @Override
+ public void onError(int errorCode) {
+ synchronized (mLock) {
+ mErrorCode = errorCode;
+ Log.d(TAG, "bugreport errored.");
+ }
+ }
+
+ @Override
+ public void onFinished() {
+ synchronized (mLock) {
+ Log.d(TAG, "bugreport finished.");
+ mSuccess = true;
+ }
+ }
+
+ /* Indicates completion; and ended up with a success or error. */
+ public boolean isDone() {
+ synchronized (mLock) {
+ return (mErrorCode != -1) || mSuccess;
+ }
+ }
+
+ public int getErrorCode() {
+ synchronized (mLock) {
+ return mErrorCode;
+ }
+ }
+
+ public boolean isSuccess() {
+ synchronized (mLock) {
+ return mSuccess;
+ }
+ }
+
+ public boolean hasReceivedProgress() {
+ synchronized (mLock) {
+ return mReceivedProgress;
+ }
+ }
+ }
+
+ public static BugreportManager getBugreportManager() {
+ Context context = InstrumentationRegistry.getContext();
+ BugreportManager bm =
+ (BugreportManager) context.getSystemService(Context.BUGREPORT_SERVICE);
+ if (bm == null) {
+ throw new AssertionError("Failed to get BugreportManager");
+ }
+ return bm;
+ }
+
+ private static ParcelFileDescriptor parcelFd(String prefix, String extension) throws Exception {
+ File f = File.createTempFile(prefix, extension);
+ f.setReadable(true, true);
+ f.setWritable(true, true);
+
+ return ParcelFileDescriptor.open(f,
+ ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_APPEND);
+ }
+
+ private static void dropPermissions() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ private static void getPermissions() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(Manifest.permission.DUMP);
+ }
+
+ private static void assertFdIsClosed(ParcelFileDescriptor pfd) {
+ try {
+ int fd = pfd.getFd();
+ fail("Expected ParcelFileDescriptor argument to be closed, but got: " + fd);
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ private static void assertFdsAreClosed(ParcelFileDescriptor... pfds) {
+ for (int i = 0; i < pfds.length; i++) {
+ assertFdIsClosed(pfds[i]);
+ }
+ }
+
+ private static long now() {
+ return System.currentTimeMillis();
+ }
+
+ private static boolean shouldTimeout(long startTimeMs) {
+ return now() - startTimeMs >= BUGREPORT_TIMEOUT_MS;
+ }
+
+ private static void waitTillDoneOrTimeout(BugreportCallbackImpl callback) throws Exception {
+ long startTimeMs = now();
+ while (!callback.isDone()) {
+ Thread.sleep(1000 /* 1s */);
+ if (shouldTimeout(startTimeMs)) {
+ break;
+ }
+ Log.d(TAG, "Waited " + (now() - startTimeMs) + "ms");
+ }
+ }
+
+ /*
+ * Returns a {@link BugreportParams} for wifi only bugreport.
+ *
+ * <p>Wifi bugreports have minimal content and are fast to run. They also suppress progress
+ * updates.
+ */
+ private static BugreportParams wifi() {
+ return new BugreportParams(BugreportParams.BUGREPORT_MODE_WIFI);
+ }
+
+ /*
+ * Returns a {@link BugreportParams} for interactive bugreport that offers progress updates.
+ *
+ * <p>This is the typical bugreport taken by users. This can take on the order of minutes to
+ * finish.
+ */
+ private static BugreportParams interactive() {
+ return new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE);
+ }
+}
diff --git a/core/tests/utiltests/Android.mk b/core/tests/utiltests/Android.mk
index 343c07af51df..9ef73e9aad93 100644
--- a/core/tests/utiltests/Android.mk
+++ b/core/tests/utiltests/Android.mk
@@ -18,6 +18,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
androidx.test.rules \
frameworks-base-testutils \
mockito-target-minus-junit4 \
+ androidx.test.ext.junit
LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock
diff --git a/core/tests/utiltests/jni/android_util_MemoryIntArrayTest.cpp b/core/tests/utiltests/jni/android_util_MemoryIntArrayTest.cpp
index 4b14284fdea5..57ee2d5f6cbb 100644
--- a/core/tests/utiltests/jni/android_util_MemoryIntArrayTest.cpp
+++ b/core/tests/utiltests/jni/android_util_MemoryIntArrayTest.cpp
@@ -21,6 +21,36 @@
#include <sys/ioctl.h>
#include <sys/mman.h>
+jint android_util_MemoryIntArrayTest_createAshmem(__attribute__((unused)) JNIEnv* env,
+ __attribute__((unused)) jobject clazz,
+ jstring name, jint size)
+{
+
+ if (name == NULL) {
+ return -1;
+ }
+
+ if (size < 0) {
+ return -1;
+ }
+
+ const char* nameStr = env->GetStringUTFChars(name, NULL);
+ const int ashmemSize = sizeof(std::atomic_int) * size;
+ int fd = ashmem_create_region(nameStr, ashmemSize);
+ env->ReleaseStringUTFChars(name, nameStr);
+
+ if (fd < 0) {
+ return -1;
+ }
+
+ int setProtResult = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
+ if (setProtResult < 0) {
+ return -1;
+ }
+
+ return fd;
+}
+
void android_util_MemoryIntArrayTest_setAshmemSize(__attribute__((unused)) JNIEnv* env,
__attribute__((unused)) jobject clazz, jint fd, jint size)
{
diff --git a/core/tests/utiltests/jni/registration.cpp b/core/tests/utiltests/jni/registration.cpp
index d4fc2fbb83fc..0c84d98e9de9 100644
--- a/core/tests/utiltests/jni/registration.cpp
+++ b/core/tests/utiltests/jni/registration.cpp
@@ -16,14 +16,25 @@
#include <jni.h>
+extern jint android_util_MemoryIntArrayTest_createAshmem(JNIEnv* env,
+ jobject clazz, jstring name, jint size);
extern void android_util_MemoryIntArrayTest_setAshmemSize(JNIEnv* env,
jobject clazz, jint fd, jint size);
extern "C" {
+ JNIEXPORT jint JNICALL Java_android_util_MemoryIntArrayTest_nativeCreateAshmem(
+ JNIEnv * env, jobject obj, jstring name, jint size);
JNIEXPORT void JNICALL Java_android_util_MemoryIntArrayTest_nativeSetAshmemSize(
JNIEnv * env, jobject obj, jint fd, jint size);
};
+JNIEXPORT jint JNICALL Java_android_util_MemoryIntArrayTest_nativeCreateAshmem(
+ __attribute__((unused)) JNIEnv * env,__attribute__((unused)) jobject obj,
+ jstring name, jint size)
+{
+ return android_util_MemoryIntArrayTest_createAshmem(env, obj, name, size);
+}
+
JNIEXPORT void JNICALL Java_android_util_MemoryIntArrayTest_nativeSetAshmemSize(
__attribute__((unused)) JNIEnv * env,__attribute__((unused)) jobject obj,
jint fd, jint size)
diff --git a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
index 2daefe74eb12..1966e122ee5b 100644
--- a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
+++ b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
@@ -23,9 +23,8 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.os.Parcel;
-import android.os.ParcelFileDescriptor;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import libcore.io.IoUtils;
@@ -36,6 +35,8 @@ import java.lang.reflect.Field;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
@RunWith(AndroidJUnit4.class)
public class MemoryIntArrayTest {
static {
@@ -255,11 +256,13 @@ public class MemoryIntArrayTest {
// Create a MemoryIntArray to muck with
MemoryIntArray array = new MemoryIntArray(1);
- // Grab the internal ashmem fd.
- Field fdField = MemoryIntArray.class.getDeclaredField("mFd");
- fdField.setAccessible(true);
- int fd = ((ParcelFileDescriptor)fdField.get(array)).getFd();
- assertTrue("fd must be valid", fd != -1);
+ // Create the fd to stuff in the MemoryIntArray
+ final int fd = nativeCreateAshmem("foo", 1);
+
+ // Replace the fd with our ahsmem region
+ Field fdFiled = MemoryIntArray.class.getDeclaredField("mFd");
+ fdFiled.setAccessible(true);
+ fdFiled.set(array, fd);
CountDownLatch countDownLatch = new CountDownLatch(2);
@@ -294,9 +297,10 @@ public class MemoryIntArrayTest {
}
if (!success) {
- fail("MemoryIntArray should catch ashmem size changing under it");
+ fail("MemoryIntArray should catch ahshmem size changing under it");
}
}
+ private native int nativeCreateAshmem(String name, int size);
private native void nativeSetAshmemSize(int fd, int size);
}
diff --git a/data/etc/com.android.dialer.xml b/data/etc/com.android.dialer.xml
index ccdb21fa5040..405279f8b1a4 100644
--- a/data/etc/com.android.dialer.xml
+++ b/data/etc/com.android.dialer.xml
@@ -24,5 +24,7 @@
<permission name="android.permission.STOP_APP_SWITCHES"/>
<permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
<permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
+ <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
+ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
</privapp-permissions>
</permissions>
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 11d635eaba12..297153d09eca 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -637,8 +637,7 @@ public class GradientDrawable extends Drawable {
* @see #setOrientation(Orientation)
*/
public Orientation getOrientation() {
- updateGradientStateOrientation();
- return mGradientState.mOrientation;
+ return mGradientState.getOrientation();
}
/**
@@ -654,10 +653,7 @@ public class GradientDrawable extends Drawable {
* @see #getOrientation()
*/
public void setOrientation(Orientation orientation) {
- // Update the angle here so that subsequent attempts to obtain the orientation
- // from the angle overwrite previously configured values during inflation
- mGradientState.mAngle = getAngleFromOrientation(orientation);
- mGradientState.mOrientation = orientation;
+ mGradientState.setOrientation(orientation);
mGradientIsDirty = true;
invalidateSelf();
}
@@ -1246,76 +1242,6 @@ public class GradientDrawable extends Drawable {
}
/**
- * Update the orientation of the gradient based on the given angle only if the type is
- * {@link #LINEAR_GRADIENT}
- */
- private void updateGradientStateOrientation() {
- if (mGradientState.mGradient == LINEAR_GRADIENT) {
- int angle = mGradientState.mAngle;
- if (angle % 45 != 0) {
- throw new IllegalArgumentException("Linear gradient requires 'angle' attribute to "
- + "be a multiple of 45");
- }
-
- Orientation orientation;
- switch (angle) {
- case 0:
- orientation = Orientation.LEFT_RIGHT;
- break;
- case 45:
- orientation = Orientation.BL_TR;
- break;
- case 90:
- orientation = Orientation.BOTTOM_TOP;
- break;
- case 135:
- orientation = Orientation.BR_TL;
- break;
- case 180:
- orientation = Orientation.RIGHT_LEFT;
- break;
- case 225:
- orientation = Orientation.TR_BL;
- break;
- case 270:
- orientation = Orientation.TOP_BOTTOM;
- break;
- case 315:
- orientation = Orientation.TL_BR;
- break;
- default:
- // Should not get here as exception is thrown above if angle is not multiple
- // of 45 degrees
- orientation = Orientation.LEFT_RIGHT;
- break;
- }
- mGradientState.mOrientation = orientation;
- }
- }
-
- private int getAngleFromOrientation(Orientation orientation) {
- switch (orientation) {
- default:
- case LEFT_RIGHT:
- return 0;
- case BL_TR:
- return 45;
- case BOTTOM_TOP:
- return 90;
- case BR_TL:
- return 135;
- case RIGHT_LEFT:
- return 180;
- case TR_BL:
- return 225;
- case TOP_BOTTOM:
- return 270;
- case TL_BR:
- return 315;
- }
- }
-
- /**
* This checks mGradientIsDirty, and if it is true, recomputes both our drawing
* rectangle (mRect) and the gradient itself, since it depends on our
* rectangle too.
@@ -1344,8 +1270,7 @@ public class GradientDrawable extends Drawable {
if (st.mGradient == LINEAR_GRADIENT) {
final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f;
- updateGradientStateOrientation();
- switch (st.mOrientation) {
+ switch (st.getOrientation()) {
case TOP_BOTTOM:
x0 = r.left; y0 = r.top;
x1 = x0; y1 = level * r.bottom;
@@ -2056,7 +1981,7 @@ public class GradientDrawable extends Drawable {
int[] mAttrPadding;
public GradientState(Orientation orientation, int[] gradientColors) {
- mOrientation = orientation;
+ setOrientation(orientation);
setGradientColors(gradientColors);
}
@@ -2259,6 +2184,93 @@ public class GradientDrawable extends Drawable {
mCenterY = y;
}
+ public void setOrientation(Orientation orientation) {
+ // Update the angle here so that subsequent attempts to obtain the orientation
+ // from the angle overwrite previously configured values during inflation
+ mAngle = getAngleFromOrientation(orientation);
+ mOrientation = orientation;
+ }
+
+ @NonNull
+ public Orientation getOrientation() {
+ updateGradientStateOrientation();
+ return mOrientation;
+ }
+
+ /**
+ * Update the orientation of the gradient based on the given angle only if the type is
+ * {@link #LINEAR_GRADIENT}
+ */
+ private void updateGradientStateOrientation() {
+ if (mGradient == LINEAR_GRADIENT) {
+ int angle = mAngle;
+ if (angle % 45 != 0) {
+ throw new IllegalArgumentException("Linear gradient requires 'angle' attribute "
+ + "to be a multiple of 45");
+ }
+
+ Orientation orientation;
+ switch (angle) {
+ case 0:
+ orientation = Orientation.LEFT_RIGHT;
+ break;
+ case 45:
+ orientation = Orientation.BL_TR;
+ break;
+ case 90:
+ orientation = Orientation.BOTTOM_TOP;
+ break;
+ case 135:
+ orientation = Orientation.BR_TL;
+ break;
+ case 180:
+ orientation = Orientation.RIGHT_LEFT;
+ break;
+ case 225:
+ orientation = Orientation.TR_BL;
+ break;
+ case 270:
+ orientation = Orientation.TOP_BOTTOM;
+ break;
+ case 315:
+ orientation = Orientation.TL_BR;
+ break;
+ default:
+ // Should not get here as exception is thrown above if angle is not multiple
+ // of 45 degrees
+ orientation = Orientation.LEFT_RIGHT;
+ break;
+ }
+ mOrientation = orientation;
+ }
+ }
+
+ private int getAngleFromOrientation(@Nullable Orientation orientation) {
+ if (orientation != null) {
+ switch (orientation) {
+ default:
+ case LEFT_RIGHT:
+ return 0;
+ case BL_TR:
+ return 45;
+ case BOTTOM_TOP:
+ return 90;
+ case BR_TL:
+ return 135;
+ case RIGHT_LEFT:
+ return 180;
+ case TR_BL:
+ return 225;
+ case TOP_BOTTOM:
+ return 270;
+ case TL_BR:
+ return 315;
+ }
+ } else {
+ return 0;
+ }
+ }
+
public void setGradientColors(@Nullable int[] colors) {
mGradientColors = colors;
mSolidColors = null;
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 62fd48940870..5173f638068d 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -167,8 +167,6 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe
LOG_ALWAYS_FATAL_IF(physDeviceProperties.apiVersion < VK_MAKE_VERSION(1, 1, 0));
mDriverVersion = physDeviceProperties.driverVersion;
- mIsQualcomm = physDeviceProperties.vendorID == 20803;
-
// query to get the initial queue props size
uint32_t queueCount;
mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, nullptr);
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 31de8030c4c1..dd3c6d0dba81 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -179,13 +179,6 @@ private:
SwapBehavior mSwapBehavior = SwapBehavior::Discard;
GrVkExtensions mExtensions;
uint32_t mDriverVersion = 0;
-
- // TODO: Remove once fix has landed. Temporaryly needed for workaround for setting up AHB
- // surfaces on Qualcomm. Currently if you don't use VkSwapchain Qualcomm is not setting
- // reporting that we need to use one of their private vendor usage bits which greatly effects
- // performance if it is not used.
- bool mIsQualcomm = false;
- bool isQualcomm() const { return mIsQualcomm; }
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index df6b9ed2cdcb..b2cc23e76b8a 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -297,11 +297,6 @@ VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode,
native_window_get_consumer_usage(window, &consumerUsage);
windowInfo.windowUsageFlags = consumerUsage | hwbUsage.androidHardwareBufferUsage;
- if (vkManager.isQualcomm()) {
- windowInfo.windowUsageFlags =
- windowInfo.windowUsageFlags | AHARDWAREBUFFER_USAGE_VENDOR_0;
- }
-
/*
* Now we attempt to modify the window!
*/
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 5de56c718570..b3c2bb78862a 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -52,6 +52,7 @@ import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.Objects;
import java.util.function.ToIntFunction;
/**
@@ -369,10 +370,12 @@ public class ThumbnailUtils {
// If we're okay with something larger than native format, just
// return a frame without up-scaling it
if (size.getWidth() > width && size.getHeight() > height) {
- return mmr.getFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC);
+ return Objects.requireNonNull(
+ mmr.getFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC));
} else {
- return mmr.getScaledFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC,
- size.getWidth(), size.getHeight());
+ return Objects.requireNonNull(
+ mmr.getScaledFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC,
+ size.getWidth(), size.getHeight()));
}
} catch (RuntimeException e) {
throw new IOException("Failed to create thumbnail", e);
diff --git a/packages/CaptivePortalLogin/Android.bp b/packages/CaptivePortalLogin/Android.bp
index a345091aa71e..1f6c2aef657c 100644
--- a/packages/CaptivePortalLogin/Android.bp
+++ b/packages/CaptivePortalLogin/Android.bp
@@ -14,12 +14,11 @@
// limitations under the License.
//
-android_app {
- name: "CaptivePortalLogin",
+java_defaults {
+ name: "CaptivePortalLoginDefaults",
srcs: ["src/**/*.java"],
sdk_version: "system_current",
min_sdk_version: "28",
- certificate: "networkstack",
static_libs: [
"androidx.legacy_legacy-support-v4",
"metrics-constants-protos",
@@ -27,3 +26,18 @@ android_app {
],
manifest: "AndroidManifest.xml",
}
+
+android_app {
+ name: "CaptivePortalLogin",
+ defaults: ["CaptivePortalLoginDefaults"],
+ certificate: "networkstack",
+}
+
+// Alternative CaptivePortalLogin signed with the platform cert, to use
+// with InProcessNetworkStack.
+android_app {
+ name: "PlatformCaptivePortalLogin",
+ defaults: ["CaptivePortalLoginDefaults"],
+ certificate: "platform",
+ overrides: ["CaptivePortalLogin"],
+}
diff --git a/packages/CarSystemUI/AndroidManifest.xml b/packages/CarSystemUI/AndroidManifest.xml
index 4e8a3a3885a7..195d4fee5162 100644
--- a/packages/CarSystemUI/AndroidManifest.xml
+++ b/packages/CarSystemUI/AndroidManifest.xml
@@ -19,6 +19,6 @@
package="com.android.systemui"
android:sharedUserId="android.uid.systemui"
coreApp="true">
-
-
+ <!-- This permission is required to monitor car power state. -->
+ <uses-permission android:name="android.car.permission.CAR_POWER" />
</manifest>
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index febf8b801726..f5dab01d1b09 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -25,6 +25,7 @@ import android.app.ActivityTaskManager;
import android.car.Car;
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.CarUxRestrictionsManager;
+import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -121,12 +122,17 @@ public class CarStatusBar extends StatusBar implements
private boolean mDeviceIsProvisioned = true;
private HvacController mHvacController;
private DrivingStateHelper mDrivingStateHelper;
- private static FlingAnimationUtils sFlingAnimationUtils;
+ private PowerManagerHelper mPowerManagerHelper;
+ private FlingAnimationUtils mFlingAnimationUtils;
private SwitchToGuestTimer mSwitchToGuestTimer;
+ private NotificationDataManager mNotificationDataManager;
+ private NotificationClickHandlerFactory mNotificationClickHandlerFactory;
// The container for the notifications.
private CarNotificationView mNotificationView;
private RecyclerView mNotificationList;
+ // The controller for the notification view.
+ private NotificationViewController mNotificationViewController;
// The state of if the notification list is currently showing the bottom.
private boolean mNotificationListAtBottom;
// Was the notification list at the bottom when the user first touched the screen
@@ -156,6 +162,20 @@ public class CarStatusBar extends StatusBar implements
// If notification shade is being swiped vertically to close.
private boolean mIsSwipingVerticallyToClose;
+ private final CarPowerStateListener mCarPowerStateListener =
+ (int state) -> {
+ // When the car powers on, clear all notifications and mute/unread states.
+ Log.d(TAG, "New car power state: " + state);
+ if (state == CarPowerStateListener.ON) {
+ if (mNotificationClickHandlerFactory != null) {
+ mNotificationClickHandlerFactory.clearAllNotifications();
+ }
+ if (mNotificationDataManager != null) {
+ mNotificationDataManager.clearAll();
+ }
+ }
+ };
+
@Override
public void start() {
// get the provisioned state before calling the parent class since it's that flow that
@@ -172,7 +192,7 @@ public class CarStatusBar extends StatusBar implements
R.integer.notification_settle_open_percentage);
mSettleClosePercentage = mContext.getResources().getInteger(
R.integer.notification_settle_close_percentage);
- sFlingAnimationUtils = new FlingAnimationUtils(mContext,
+ mFlingAnimationUtils = new FlingAnimationUtils(mContext,
FLING_ANIMATION_MAX_TIME, FLING_SPEED_UP_FACTOR);
createBatteryController();
@@ -204,6 +224,9 @@ public class CarStatusBar extends StatusBar implements
mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged);
mDrivingStateHelper.connectToCarService();
+ mPowerManagerHelper = new PowerManagerHelper(mContext, mCarPowerStateListener);
+ mPowerManagerHelper.connectToCarService();
+
mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
}
@@ -308,7 +331,6 @@ public class CarStatusBar extends StatusBar implements
}
}
-
@Override
protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
super.makeStatusBarView(result);
@@ -407,7 +429,7 @@ public class CarStatusBar extends StatusBar implements
}
);
- NotificationClickHandlerFactory clickHandlerFactory = new NotificationClickHandlerFactory(
+ mNotificationClickHandlerFactory = new NotificationClickHandlerFactory(
mBarService,
launchResult -> {
if (launchResult == ActivityManager.START_TASK_TO_FRONT
@@ -422,26 +444,27 @@ public class CarStatusBar extends StatusBar implements
CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper =
new CarUxRestrictionManagerWrapper();
carUxRestrictionManagerWrapper.setCarUxRestrictionsManager(carUxRestrictionsManager);
- NotificationDataManager notificationDataManager = new NotificationDataManager();
- notificationDataManager.setOnUnseenCountUpdateListener(
+ mNotificationDataManager = new NotificationDataManager();
+ mNotificationDataManager.setOnUnseenCountUpdateListener(
() -> {
// TODO: Update Notification Icon based on unseen count
Log.d(TAG, "unseen count: " +
- notificationDataManager.getUnseenNotificationCount());
+ mNotificationDataManager.getUnseenNotificationCount());
});
CarHeadsUpNotificationManager carHeadsUpNotificationManager =
- new CarSystemUIHeadsUpNotificationManager(mContext, clickHandlerFactory,
- notificationDataManager);
+ new CarSystemUIHeadsUpNotificationManager(mContext,
+ mNotificationClickHandlerFactory, mNotificationDataManager);
+ mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager);
carNotificationListener.registerAsSystemService(mContext, carUxRestrictionManagerWrapper,
- carHeadsUpNotificationManager, notificationDataManager);
+ carHeadsUpNotificationManager, mNotificationDataManager);
mNotificationView = mStatusBarWindow.findViewById(R.id.notification_view);
View glassPane = mStatusBarWindow.findViewById(R.id.glass_pane);
- mNotificationView.setClickHandlerFactory(clickHandlerFactory);
- mNotificationView.setNotificationDataManager(notificationDataManager);
+ mNotificationView.setClickHandlerFactory(mNotificationClickHandlerFactory);
+ mNotificationView.setNotificationDataManager(mNotificationDataManager);
// The glass pane is used to view touch events before passed to the notification list.
// This allows us to initialize gesture listeners and detect when to close the notifications
@@ -523,12 +546,12 @@ public class CarStatusBar extends StatusBar implements
}
});
- NotificationViewController mNotificationViewController = new NotificationViewController(
+ mNotificationViewController = new NotificationViewController(
mNotificationView,
PreprocessingManager.getInstance(mContext),
carNotificationListener,
carUxRestrictionManagerWrapper,
- notificationDataManager);
+ mNotificationDataManager);
mNotificationViewController.enable();
}
@@ -630,16 +653,18 @@ public class CarStatusBar extends StatusBar implements
mStatusBarWindowController.setPanelVisible(false);
mNotificationView.setVisibility(View.INVISIBLE);
mNotificationList.setClipBounds(null);
+ mNotificationViewController.setIsInForeground(false);
// let the status bar know that the panel is closed
setPanelExpanded(false);
} else {
+ mNotificationViewController.setIsInForeground(true);
// let the status bar know that the panel is open
mNotificationView.setVisibleNotificationsAsSeen();
setPanelExpanded(true);
}
}
});
- sFlingAnimationUtils.apply(animator, from, to, Math.abs(velocity));
+ mFlingAnimationUtils.apply(animator, from, to, Math.abs(velocity));
animator.start();
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java
new file mode 100644
index 000000000000..8de1439c3306
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.car;
+
+import android.annotation.NonNull;
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.hardware.power.CarPowerManager;
+import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.util.Log;
+
+/**
+ * Helper class for connecting to the {@link CarPowerManager} and listening for power state changes.
+ */
+public class PowerManagerHelper {
+ public static final String TAG = "PowerManagerHelper";
+
+ private final Context mContext;
+ private final CarPowerStateListener mCarPowerStateListener;
+
+ private Car mCar;
+ private CarPowerManager mCarPowerManager;
+
+ private final ServiceConnection mCarConnectionListener =
+ new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.d(TAG, "Car Service connected");
+ try {
+ mCarPowerManager = (CarPowerManager) mCar.getCarManager(Car.POWER_SERVICE);
+ if (mCarPowerManager != null) {
+ mCarPowerManager.setListener(mCarPowerStateListener);
+ } else {
+ Log.e(TAG, "CarPowerManager service not available");
+ }
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car not connected", e);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ destroyCarPowerManager();
+ }
+ };
+
+ PowerManagerHelper(Context context, @NonNull CarPowerStateListener listener) {
+ mContext = context;
+ mCarPowerStateListener = listener;
+ }
+
+ /**
+ * Connect to Car service.
+ */
+ void connectToCarService() {
+ mCar = Car.createCar(mContext, mCarConnectionListener);
+ if (mCar != null) {
+ mCar.connect();
+ }
+ }
+
+ /**
+ * Disconnects from Car service.
+ */
+ void disconnectFromCarService() {
+ if (mCar != null) {
+ mCar.disconnect();
+ }
+ }
+
+ private void destroyCarPowerManager() {
+ if (mCarPowerManager != null) {
+ mCarPowerManager.clearListener();
+ }
+ }
+}
diff --git a/packages/NetworkPermissionConfig/Android.bp b/packages/NetworkPermissionConfig/Android.bp
index d0d3276c0e32..6e50459a1dd3 100644
--- a/packages/NetworkPermissionConfig/Android.bp
+++ b/packages/NetworkPermissionConfig/Android.bp
@@ -14,15 +14,28 @@
// limitations under the License.
//
-// Stub APK to define permissions for NetworkStack
-android_app {
- name: "NetworkPermissionConfig",
+java_defaults {
+ name: "NetworkPermissionConfigDefaults",
// TODO: mark app as hasCode=false in manifest once soong stops complaining about apps without
// a classes.dex.
srcs: ["src/**/*.java"],
platform_apis: true,
min_sdk_version: "28",
- certificate: "networkstack",
privileged: true,
manifest: "AndroidManifest.xml",
}
+
+// Stub APK to define permissions for NetworkStack
+android_app {
+ name: "NetworkPermissionConfig",
+ defaults: ["NetworkPermissionConfigDefaults"],
+ certificate: "networkstack",
+}
+
+// Alternative stub APK signed with platform certificate. To use with InProcessNetworkStack.
+android_app {
+ name: "PlatformNetworkPermissionConfig",
+ defaults: ["NetworkPermissionConfigDefaults"],
+ certificate: "platform",
+ overrides: ["NetworkPermissionConfig"],
+}
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index 62de2ba45455..e15526a571f5 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -98,8 +98,6 @@ java_defaults {
optimize: {
proguard_flags_files: ["proguard.flags"],
},
- // The permission configuration *must* be included to ensure security of the device
- required: ["NetworkPermissionConfig"],
}
// Non-updatable network stack running in the system server process for devices not using the module
@@ -108,6 +106,10 @@ android_app {
defaults: ["NetworkStackAppCommon"],
certificate: "platform",
manifest: "AndroidManifest_InProcess.xml",
+ // InProcessNetworkStack is a replacement for NetworkStack
+ overrides: ["NetworkStack"],
+ // The permission configuration *must* be included to ensure security of the device
+ required: ["PlatformNetworkPermissionConfig"],
}
// Updatable network stack packaged as an application
@@ -116,6 +118,9 @@ android_app {
defaults: ["NetworkStackAppCommon"],
certificate: "networkstack",
manifest: "AndroidManifest.xml",
+ use_embedded_native_libs: true,
+ // The permission configuration *must* be included to ensure security of the device
+ required: ["NetworkPermissionConfig"],
}
genrule {
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index 252b90fea840..bfcd6c1baba3 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -41,7 +41,7 @@
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<!-- Signature permission defined in NetworkStackStub -->
<uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" />
- <application>
+ <application android:extractNativeLibs="false">
<service android:name="com.android.server.NetworkStackService">
<intent-filter>
<action android:name="android.net.INetworkStackConnector"/>
diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java
index dc74c041c35a..266b1b047a90 100644
--- a/packages/NetworkStack/src/android/net/ip/IpClient.java
+++ b/packages/NetworkStack/src/android/net/ip/IpClient.java
@@ -372,10 +372,6 @@ public class IpClient extends StateMachine {
private boolean mMulticastFiltering;
private long mStartTimeMillis;
- /* This must match the definition in KeepaliveTracker.KeepaliveInfo */
- private static final int TYPE_NATT = 1;
- private static final int TYPE_TCP = 2;
-
/**
* Reading the snapshot is an asynchronous operation initiated by invoking
* Callback.startReadPacketFilter() and completed when the WiFi Service responds with an
@@ -705,7 +701,7 @@ public class IpClient extends StateMachine {
* keepalive offload.
*/
public void addKeepalivePacketFilter(int slot, @NonNull TcpKeepalivePacketDataParcelable pkt) {
- sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, TYPE_TCP, pkt);
+ sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */, pkt);
}
/**
@@ -714,7 +710,7 @@ public class IpClient extends StateMachine {
*/
public void addNattKeepalivePacketFilter(int slot,
@NonNull NattKeepalivePacketDataParcelable pkt) {
- sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, TYPE_NATT, pkt);
+ sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */ , pkt);
}
/**
@@ -1626,13 +1622,12 @@ public class IpClient extends StateMachine {
case CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF: {
final int slot = msg.arg1;
- final int type = msg.arg2;
if (mApfFilter != null) {
- if (type == TYPE_NATT) {
+ if (msg.obj instanceof NattKeepalivePacketDataParcelable) {
mApfFilter.addNattKeepalivePacketFilter(slot,
(NattKeepalivePacketDataParcelable) msg.obj);
- } else {
+ } else if (msg.obj instanceof TcpKeepalivePacketDataParcelable) {
mApfFilter.addTcpKeepalivePacketFilter(slot,
(TcpKeepalivePacketDataParcelable) msg.obj);
}
diff --git a/packages/NetworkStack/src/com/android/networkstack/util/DnsUtils.java b/packages/NetworkStack/src/com/android/networkstack/util/DnsUtils.java
index 85f94e17a088..4767d5574a00 100644
--- a/packages/NetworkStack/src/com/android/networkstack/util/DnsUtils.java
+++ b/packages/NetworkStack/src/com/android/networkstack/util/DnsUtils.java
@@ -55,12 +55,23 @@ public class DnsUtils {
throws UnknownHostException {
final List<InetAddress> result = new ArrayList<InetAddress>();
- result.addAll(Arrays.asList(
- getAllByName(dnsResolver, network, host, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
- timeout)));
- result.addAll(Arrays.asList(
- getAllByName(dnsResolver, network, host, TYPE_A, FLAG_NO_CACHE_LOOKUP,
- timeout)));
+ try {
+ result.addAll(Arrays.asList(
+ getAllByName(dnsResolver, network, host, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
+ timeout)));
+ } catch (UnknownHostException e) {
+ // Might happen if the host is v4-only, still need to query TYPE_A
+ }
+ try {
+ result.addAll(Arrays.asList(
+ getAllByName(dnsResolver, network, host, TYPE_A, FLAG_NO_CACHE_LOOKUP,
+ timeout)));
+ } catch (UnknownHostException e) {
+ // Might happen if the host is v6-only, still need to return AAAA answers
+ }
+ if (result.size() == 0) {
+ throw new UnknownHostException(host);
+ }
return result.toArray(new InetAddress[0]);
}
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index abfb9c8ae282..26186751c282 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -297,9 +297,10 @@ public class NetworkMonitorTest {
setOtherFallbackUrls(TEST_OTHER_FALLBACK_URL);
setFallbackSpecs(null); // Test with no fallback spec by default
when(mRandom.nextInt()).thenReturn(0);
-
+ // DNS probe timeout should not be defined more than half of HANDLER_TIMEOUT_MS. Otherwise,
+ // it will fail the test because of timeout expired for querying AAAA and A sequentially.
when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout)))
- .thenReturn(500);
+ .thenReturn(200);
doAnswer((invocation) -> {
URL url = invocation.getArgument(0);
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
index cb0b7c2bac83..98eb57300f0b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
@@ -286,8 +286,7 @@ public class SignalDrawable extends DrawableWrapper {
/** Returns the state representing empty mobile signal with the given number of levels. */
public static int getEmptyState(int numLevels) {
- // TODO empty state == 0 state. does there need to be a new drawable for this?
- return getState(0, numLevels, false);
+ return getState(0, numLevels, true);
}
/** Returns the state representing carrier change with the given number of levels. */
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
index b8e1251dd79a..6fd874989c35 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
@@ -18,11 +18,11 @@ package com.android.settingslib.location;
import android.app.AppOpsManager;
import android.content.Context;
+import android.content.PermissionChecker;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
-import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.format.DateUtils;
@@ -48,10 +48,15 @@ public class RecentLocationApps {
private static final long RECENT_TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS;
@VisibleForTesting
- static final int[] LOCATION_OPS = new int[] {
+ static final int[] LOCATION_REQUEST_OPS = new int[]{
AppOpsManager.OP_MONITOR_LOCATION,
AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
};
+ @VisibleForTesting
+ static final int[] LOCATION_PERMISSION_OPS = new int[]{
+ AppOpsManager.OP_FINE_LOCATION,
+ AppOpsManager.OP_COARSE_LOCATION,
+ };
private final PackageManager mPackageManager;
private final Context mContext;
@@ -67,11 +72,13 @@ public class RecentLocationApps {
* Fills a list of applications which queried location recently within specified time.
* Apps are sorted by recency. Apps with more recent location requests are in the front.
*/
- public List<Request> getAppList() {
+ public List<Request> getAppList(boolean showSystemApps) {
+ // Retrieve a location usage list from AppOps
+ PackageManager pm = mContext.getPackageManager();
// Retrieve a location usage list from AppOps
AppOpsManager aoManager =
(AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(LOCATION_OPS);
+ List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(LOCATION_REQUEST_OPS);
final int appOpsCount = appOps != null ? appOps.size() : 0;
@@ -83,26 +90,58 @@ public class RecentLocationApps {
for (int i = 0; i < appOpsCount; ++i) {
AppOpsManager.PackageOps ops = appOps.get(i);
- // Don't show the Android System in the list - it's not actionable for the user.
- // Also don't show apps belonging to background users except managed users.
String packageName = ops.getPackageName();
int uid = ops.getUid();
- int userId = UserHandle.getUserId(uid);
- boolean isAndroidOs =
- (uid == Process.SYSTEM_UID) && ANDROID_SYSTEM_PACKAGE_NAME.equals(packageName);
- if (isAndroidOs || !profiles.contains(new UserHandle(userId))) {
+ final UserHandle user = UserHandle.getUserHandleForUid(uid);
+
+ // Don't show apps belonging to background users except managed users.
+ if (!profiles.contains(user)) {
continue;
}
- Request request = getRequestFromOps(now, ops);
- if (request != null) {
- requests.add(request);
+
+ // Don't show apps that do not have user sensitive location permissions
+ boolean showApp = true;
+ if (!showSystemApps) {
+ for (int op : LOCATION_PERMISSION_OPS) {
+ final String permission = AppOpsManager.opToPermission(op);
+ final int permissionFlags = pm.getPermissionFlags(permission, packageName,
+ user);
+ if (PermissionChecker.checkPermission(mContext, permission, -1, uid,
+ packageName)
+ == PermissionChecker.PERMISSION_GRANTED) {
+ if ((permissionFlags
+ & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED)
+ == 0) {
+ showApp = false;
+ break;
+ }
+ } else {
+ if ((permissionFlags
+ & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) == 0) {
+ showApp = false;
+ break;
+ }
+ }
+ }
+ }
+ if (showApp) {
+ Request request = getRequestFromOps(now, ops);
+ if (request != null) {
+ requests.add(request);
+ }
}
}
return requests;
}
- public List<Request> getAppListSorted() {
- List<Request> requests = getAppList();
+ /**
+ * Gets a list of apps that requested for location recently, sorting by recency.
+ *
+ * @param showSystemApps whether includes system apps in the list.
+ * @return the list of apps that recently requested for location.
+ */
+ public List<Request> getAppListSorted(boolean showSystemApps) {
+ List<Request> requests = getAppList(showSystemApps);
// Sort the list of Requests by recency. Most recent request first.
Collections.sort(requests, Collections.reverseOrder(new Comparator<Request>() {
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index 2bfcc91bbc29..f30de130f616 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -175,9 +175,7 @@ public class DataUsageController {
private long getUsageLevel(NetworkTemplate template, long start, long end) {
try {
- final Bucket bucket = mNetworkStatsManager.querySummaryForDevice(
- getNetworkType(template), getActiveSubscriberId(),
- start, end);
+ final Bucket bucket = mNetworkStatsManager.querySummaryForDevice(template, start, end);
if (bucket != null) {
return bucket.getRxBytes() + bucket.getTxBytes();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java
index ec5a0b5cc4cd..787dc55e60f4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java
@@ -37,14 +37,14 @@ public class NetworkCycleChartDataLoader
private NetworkCycleChartDataLoader(Builder builder) {
super(builder);
- mData = new ArrayList<NetworkCycleChartData>();
+ mData = new ArrayList<>();
}
@Override
void recordUsage(long start, long end) {
try {
final NetworkStats.Bucket bucket = mNetworkStatsManager.querySummaryForDevice(
- mNetworkType, mSubId, start, end);
+ mNetworkTemplate, start, end);
final long total = bucket == null ? 0L : bucket.getRxBytes() + bucket.getTxBytes();
if (total > 0L) {
final NetworkCycleChartData.Builder builder = new NetworkCycleChartData.Builder();
@@ -81,7 +81,7 @@ public class NetworkCycleChartDataLoader
long usage = 0L;
try {
final NetworkStats.Bucket bucket = mNetworkStatsManager.querySummaryForDevice(
- mNetworkType, mSubId, bucketStart, bucketEnd);
+ mNetworkTemplate, bucketStart, bucketEnd);
if (bucket != null) {
usage = bucket.getRxBytes() + bucket.getTxBytes();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
index bd9a636883c2..43c05b8b64d6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
@@ -23,11 +23,11 @@ import android.app.usage.NetworkStats;
import android.content.Context;
import android.util.Log;
+import androidx.annotation.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.List;
-import androidx.annotation.VisibleForTesting;
-
/**
* Loader for network data usage history. It returns a list of usage data per billing cycle for the
* specific Uid(s).
@@ -44,7 +44,7 @@ public class NetworkCycleDataForUidLoader extends
super(builder);
mUids = builder.mUids;
mRetrieveDetail = builder.mRetrieveDetail;
- mData = new ArrayList<NetworkCycleDataForUid>();
+ mData = new ArrayList<>();
}
@Override
@@ -54,7 +54,7 @@ public class NetworkCycleDataForUidLoader extends
long totalForeground = 0L;
for (int uid : mUids) {
final NetworkStats stats = mNetworkStatsManager.queryDetailsForUid(
- mNetworkType, mSubId, start, end, uid);
+ mNetworkTemplate, start, end, uid);
final long usage = getTotalUsage(stats);
if (usage > 0L) {
totalUsage += usage;
@@ -100,7 +100,7 @@ public class NetworkCycleDataForUidLoader extends
private long getForegroundUsage(long start, long end, int uid) {
final NetworkStats stats = mNetworkStatsManager.queryDetailsForUidTagState(
- mNetworkType, mSubId, start, end, uid, TAG_NONE, STATE_FOREGROUND);
+ mNetworkTemplate, start, end, uid, TAG_NONE, STATE_FOREGROUND);
return getTotalUsage(stats);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
index dd6d563b3197..3e95b01824cc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
@@ -49,18 +49,14 @@ import java.util.Iterator;
public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {
private static final String TAG = "NetworkCycleDataLoader";
protected final NetworkStatsManager mNetworkStatsManager;
- protected final String mSubId;
- protected final int mNetworkType;
+ protected final NetworkTemplate mNetworkTemplate;
private final NetworkPolicy mPolicy;
- private final NetworkTemplate mNetworkTemplate;
private final ArrayList<Long> mCycles;
@VisibleForTesting
final INetworkStatsService mNetworkStatsService;
protected NetworkCycleDataLoader(Builder<?> builder) {
super(builder.mContext);
- mSubId = builder.mSubId;
- mNetworkType = builder.mNetworkType;
mNetworkTemplate = builder.mNetworkTemplate;
mCycles = builder.mCycles;
mNetworkStatsManager = (NetworkStatsManager)
@@ -180,8 +176,6 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {
public static abstract class Builder<T extends NetworkCycleDataLoader> {
private final Context mContext;
- private String mSubId;
- private int mNetworkType;
private NetworkTemplate mNetworkTemplate;
private ArrayList<Long> mCycles;
@@ -189,14 +183,8 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {
mContext = context;
}
- public Builder<T> setSubscriberId(String subId) {
- mSubId = subId;
- return this;
- }
-
public Builder<T> setNetworkTemplate(NetworkTemplate template) {
mNetworkTemplate = template;
- mNetworkType = DataUsageController.getNetworkType(template);
return this;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java
index 34e6097ea46e..ed093629686c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java
@@ -16,9 +16,10 @@
package com.android.settingslib.net;
-import android.app.usage.NetworkStatsManager;
import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
import android.content.Context;
+import android.net.NetworkTemplate;
import android.os.RemoteException;
import android.util.Log;
@@ -33,15 +34,13 @@ public class NetworkStatsSummaryLoader extends AsyncTaskLoader<NetworkStats> {
private final NetworkStatsManager mNetworkStatsManager;
private final long mStart;
private final long mEnd;
- private final String mSubId;
- private final int mNetworkType;
+ private final NetworkTemplate mNetworkTemplate;
private NetworkStatsSummaryLoader(Builder builder) {
super(builder.mContext);
mStart = builder.mStart;
mEnd = builder.mEnd;
- mSubId = builder.mSubId;
- mNetworkType = builder.mNetworkType;
+ mNetworkTemplate = builder.mNetworkTemplate;
mNetworkStatsManager = (NetworkStatsManager)
builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
}
@@ -55,7 +54,7 @@ public class NetworkStatsSummaryLoader extends AsyncTaskLoader<NetworkStats> {
@Override
public NetworkStats loadInBackground() {
try {
- return mNetworkStatsManager.querySummary(mNetworkType, mSubId, mStart, mEnd);
+ return mNetworkStatsManager.querySummary(mNetworkTemplate, mStart, mEnd);
} catch (RemoteException e) {
Log.e(TAG, "Exception querying network detail.", e);
return null;
@@ -78,8 +77,7 @@ public class NetworkStatsSummaryLoader extends AsyncTaskLoader<NetworkStats> {
private final Context mContext;
private long mStart;
private long mEnd;
- private String mSubId;
- private int mNetworkType;
+ private NetworkTemplate mNetworkTemplate;
public Builder(Context context) {
mContext = context;
@@ -95,13 +93,11 @@ public class NetworkStatsSummaryLoader extends AsyncTaskLoader<NetworkStats> {
return this;
}
- public Builder setSubscriberId(String subId) {
- mSubId = subId;
- return this;
- }
-
- public Builder setNetworkType(int networkType) {
- mNetworkType = networkType;
+ /**
+ * Set {@link NetworkTemplate} for builder
+ */
+ public Builder setNetworkTemplate(NetworkTemplate template) {
+ mNetworkTemplate = template;
return this;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 4d332b9a95b4..e28c612453b4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -180,7 +180,8 @@ public class AccessPoint implements Comparable<AccessPoint> {
public static final int SECURITY_SAE = 5;
public static final int SECURITY_EAP_SUITE_B = 6;
public static final int SECURITY_PSK_SAE_TRANSITION = 7;
- public static final int SECURITY_MAX_VAL = 8; // Has to be the last
+ public static final int SECURITY_OWE_TRANSITION = 8;
+ public static final int SECURITY_MAX_VAL = 9; // Has to be the last
private static final int PSK_UNKNOWN = 0;
private static final int PSK_WPA = 1;
@@ -869,6 +870,12 @@ public class AccessPoint implements Comparable<AccessPoint> {
return concise ? context.getString(R.string.wifi_security_short_sae) :
context.getString(R.string.wifi_security_sae);
}
+ case SECURITY_OWE_TRANSITION:
+ if (mConfig != null && getSecurity(mConfig) == SECURITY_OWE) {
+ return concise ? context.getString(R.string.wifi_security_short_owe) :
+ context.getString(R.string.wifi_security_owe);
+ }
+ return concise ? "" : context.getString(R.string.wifi_security_none);
case SECURITY_OWE:
return concise ? context.getString(R.string.wifi_security_short_owe) :
context.getString(R.string.wifi_security_owe);
@@ -1179,7 +1186,8 @@ public class AccessPoint implements Comparable<AccessPoint> {
* Can only be called for unsecured networks.
*/
public void generateOpenNetworkConfig() {
- if ((security != SECURITY_NONE) && (security != SECURITY_OWE)) {
+ if ((security != SECURITY_NONE) && (security != SECURITY_OWE)
+ && (security != SECURITY_OWE_TRANSITION)) {
throw new IllegalStateException();
}
if (mConfig != null)
@@ -1187,7 +1195,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
mConfig = new WifiConfiguration();
mConfig.SSID = AccessPoint.convertToQuotedString(ssid);
- if (security == SECURITY_NONE) {
+ if (security == SECURITY_NONE || !getWifiManager().isEasyConnectSupported()) {
mConfig.allowedKeyManagement.set(KeyMgmt.NONE);
} else {
mConfig.allowedKeyManagement.set(KeyMgmt.OWE);
@@ -1229,6 +1237,9 @@ public class AccessPoint implements Comparable<AccessPoint> {
private static final String sPskSuffix = "," + String.valueOf(SECURITY_PSK);
private static final String sSaeSuffix = "," + String.valueOf(SECURITY_SAE);
private static final String sPskSaeSuffix = "," + String.valueOf(SECURITY_PSK_SAE_TRANSITION);
+ private static final String sOweSuffix = "," + String.valueOf(SECURITY_OWE);
+ private static final String sOpenSuffix = "," + String.valueOf(SECURITY_NONE);
+ private static final String sOweTransSuffix = "," + String.valueOf(SECURITY_OWE_TRANSITION);
private boolean isKeyEqual(String compareTo) {
if (mKey == null) {
@@ -1240,7 +1251,15 @@ public class AccessPoint implements Comparable<AccessPoint> {
// Special handling for PSK-SAE transition mode. If the AP has advertised both,
// we compare the key with both PSK and SAE for a match.
return TextUtils.equals(mKey.substring(0, mKey.lastIndexOf(',')),
- compareTo.substring(0, mKey.lastIndexOf(',')));
+ compareTo.substring(0, compareTo.lastIndexOf(',')));
+ }
+ }
+ if (compareTo.endsWith(sOpenSuffix) || compareTo.endsWith(sOweSuffix)) {
+ if (mKey.endsWith(sOweTransSuffix)) {
+ // Special handling for OWE/Open networks. If AP advertises OWE in transition mode
+ // and we have an Open network saved, allow this connection to be established.
+ return TextUtils.equals(mKey.substring(0, mKey.lastIndexOf(',')),
+ compareTo.substring(0, compareTo.lastIndexOf(',')));
}
}
return mKey.equals(compareTo);
@@ -1579,10 +1598,11 @@ public class AccessPoint implements Comparable<AccessPoint> {
return SECURITY_EAP_SUITE_B;
} else if (result.capabilities.contains("EAP")) {
return SECURITY_EAP;
+ } else if (result.capabilities.contains("OWE_TRANSITION")) {
+ return SECURITY_OWE_TRANSITION;
} else if (result.capabilities.contains("OWE")) {
return SECURITY_OWE;
}
-
return SECURITY_NONE;
}
@@ -1628,6 +1648,8 @@ public class AccessPoint implements Comparable<AccessPoint> {
return "OWE";
} else if (security == SECURITY_PSK_SAE_TRANSITION) {
return "PSK+SAE";
+ } else if (security == SECURITY_OWE_TRANSITION) {
+ return "OWE_TRANSITION";
}
return "NONE";
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 6269a717b333..dae546497aba 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -201,7 +201,8 @@ public class AccessPointPreference extends Preference {
return;
}
if ((mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE)
- && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE)) {
+ && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE)
+ && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE_TRANSITION)) {
mFrictionSld.setState(STATE_SECURED);
} else if (mAccessPoint.isMetered()) {
mFrictionSld.setState(STATE_METERED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
index 8bd5fd2163ae..7a553fc91ff6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
@@ -16,8 +16,8 @@ import android.content.res.Resources;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
-
import android.util.LongSparseLongArray;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -75,7 +75,8 @@ public class RecentLocationAppsTest {
long[] testRequestTime = {ONE_MIN_AGO, TWENTY_THREE_HOURS_AGO, TWO_DAYS_AGO};
List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime);
- when(mAppOpsManager.getPackagesForOps(RecentLocationApps.LOCATION_OPS)).thenReturn(appOps);
+ when(mAppOpsManager.getPackagesForOps(RecentLocationApps.LOCATION_REQUEST_OPS)).thenReturn(
+ appOps);
mockTestApplicationInfos(mTestUserId, TEST_PACKAGE_NAMES);
mRecentLocationApps = new RecentLocationApps(mContext);
@@ -83,7 +84,7 @@ public class RecentLocationAppsTest {
@Test
public void testGetAppList_shouldFilterRecentApps() {
- List<RecentLocationApps.Request> requests = mRecentLocationApps.getAppList();
+ List<RecentLocationApps.Request> requests = mRecentLocationApps.getAppList(true);
// Only two of the apps have requested location within 15 min.
assertThat(requests).hasSize(2);
// Make sure apps are ordered by recency
@@ -107,11 +108,12 @@ public class RecentLocationAppsTest {
{ONE_MIN_AGO, TWENTY_THREE_HOURS_AGO, TWO_DAYS_AGO, ONE_MIN_AGO};
List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime);
appOps.add(androidSystemPackageOps);
- when(mAppOpsManager.getPackagesForOps(RecentLocationApps.LOCATION_OPS)).thenReturn(appOps);
+ when(mAppOpsManager.getPackagesForOps(RecentLocationApps.LOCATION_REQUEST_OPS)).thenReturn(
+ appOps);
mockTestApplicationInfos(
Process.SYSTEM_UID, RecentLocationApps.ANDROID_SYSTEM_PACKAGE_NAME);
- List<RecentLocationApps.Request> requests = mRecentLocationApps.getAppList();
+ List<RecentLocationApps.Request> requests = mRecentLocationApps.getAppList(true);
// Android OS shouldn't show up in the list of apps.
assertThat(requests).hasSize(2);
// Make sure apps are ordered by recency
@@ -133,7 +135,7 @@ public class RecentLocationAppsTest {
private List<PackageOps> createTestPackageOpsList(String[] packageNameList, long[] time) {
List<PackageOps> packageOpsList = new ArrayList<>();
- for (int i = 0; i < packageNameList.length ; i++) {
+ for (int i = 0; i < packageNameList.length; i++) {
PackageOps packageOps = createPackageOps(
packageNameList[i],
TEST_UID,
@@ -156,11 +158,11 @@ public class RecentLocationAppsTest {
private OpEntry createOpEntryWithTime(int op, long time, int duration) {
final LongSparseLongArray accessTimes = new LongSparseLongArray();
accessTimes.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_TOP,
- AppOpsManager.OP_FLAG_SELF), time);
+ AppOpsManager.OP_FLAG_SELF), time);
final LongSparseLongArray durations = new LongSparseLongArray();
durations.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_TOP,
- AppOpsManager.OP_FLAG_SELF), duration);
+ AppOpsManager.OP_FLAG_SELF), duration);
return new OpEntry(op, false, AppOpsManager.MODE_ALLOWED, accessTimes,
- null /*rejectTimes*/, durations, null /* proxyUids */, null /* proxyPackages */);
+ null /*rejectTimes*/, durations, null /* proxyUids */, null /* proxyPackages */);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
index a28bb6ce44c5..3da5e766c389 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
@@ -31,7 +31,6 @@ import static org.mockito.Mockito.when;
import android.app.usage.NetworkStats;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
-import android.net.ConnectivityManager;
import android.net.INetworkStatsSession;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
@@ -52,6 +51,7 @@ import org.robolectric.shadows.ShadowSubscriptionManager;
public class DataUsageControllerTest {
private static final String SUB_ID = "Test Subscriber";
+ private static final String SUB_ID_2 = "Test Subscriber 2";
@Mock
private INetworkStatsSession mSession;
@@ -63,6 +63,9 @@ public class DataUsageControllerTest {
private NetworkStatsManager mNetworkStatsManager;
@Mock
private Context mContext;
+ private NetworkTemplate mNetworkTemplate;
+ private NetworkTemplate mNetworkTemplate2;
+ private NetworkTemplate mWifiNetworkTemplate;
private DataUsageController mController;
private NetworkStatsHistory mNetworkStatsHistory;
@@ -83,24 +86,27 @@ public class DataUsageControllerTest {
.when(mSession).getHistoryForNetwork(any(NetworkTemplate.class), anyInt());
ShadowSubscriptionManager.setDefaultDataSubscriptionId(mDefaultSubscriptionId);
doReturn(SUB_ID).when(mTelephonyManager).getSubscriberId();
+
+ mNetworkTemplate = NetworkTemplate.buildTemplateMobileAll(SUB_ID);
+ mNetworkTemplate2 = NetworkTemplate.buildTemplateMobileAll(SUB_ID_2);
+ mWifiNetworkTemplate = NetworkTemplate.buildTemplateWifiWildcard();
}
@Test
public void getHistoricalUsageLevel_shouldQuerySummaryForDevice() throws Exception {
+ mController.getHistoricalUsageLevel(mWifiNetworkTemplate);
- mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard());
-
- verify(mNetworkStatsManager).querySummaryForDevice(eq(ConnectivityManager.TYPE_WIFI),
- eq(SUB_ID), eq(0L) /* startTime */, anyLong() /* endTime */);
+ verify(mNetworkStatsManager).querySummaryForDevice(eq(mWifiNetworkTemplate),
+ eq(0L) /* startTime */, anyLong() /* endTime */);
}
@Test
public void getHistoricalUsageLevel_noUsageData_shouldReturn0() throws Exception {
- when(mNetworkStatsManager.querySummaryForDevice(eq(ConnectivityManager.TYPE_WIFI),
- eq(SUB_ID), eq(0L) /* startTime */, anyLong() /* endTime */))
+ when(mNetworkStatsManager.querySummaryForDevice(eq(mWifiNetworkTemplate),
+ eq(0L) /* startTime */, anyLong() /* endTime */))
.thenReturn(mock(NetworkStats.Bucket.class));
- assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
- .isEqualTo(0L);
+ assertThat(mController.getHistoricalUsageLevel(mWifiNetworkTemplate))
+ .isEqualTo(0L);
}
@Test
@@ -110,10 +116,10 @@ public class DataUsageControllerTest {
final NetworkStats.Bucket bucket = mock(NetworkStats.Bucket.class);
when(bucket.getRxBytes()).thenReturn(receivedBytes);
when(bucket.getTxBytes()).thenReturn(transmittedBytes);
- when(mNetworkStatsManager.querySummaryForDevice(eq(ConnectivityManager.TYPE_WIFI),
- eq(SUB_ID), eq(0L) /* startTime */, anyLong() /* endTime */)).thenReturn(bucket);
+ when(mNetworkStatsManager.querySummaryForDevice(eq(mWifiNetworkTemplate),
+ eq(0L) /* startTime */, anyLong() /* endTime */)).thenReturn(bucket);
- assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
+ assertThat(mController.getHistoricalUsageLevel(mWifiNetworkTemplate))
.isEqualTo(receivedBytes + transmittedBytes);
}
@@ -126,9 +132,8 @@ public class DataUsageControllerTest {
final NetworkStats.Bucket defaultSubscriberBucket = mock(NetworkStats.Bucket.class);
when(defaultSubscriberBucket.getRxBytes()).thenReturn(defaultSubRx);
when(defaultSubscriberBucket.getTxBytes()).thenReturn(defaultSubTx);
- when(mNetworkStatsManager.querySummaryForDevice(eq(ConnectivityManager.TYPE_MOBILE),
- eq(SUB_ID), eq(0L)/* startTime */, anyLong() /* endTime */)).thenReturn(
- defaultSubscriberBucket);
+ when(mNetworkStatsManager.querySummaryForDevice(eq(mNetworkTemplate), eq(0L)/* startTime */,
+ anyLong() /* endTime */)).thenReturn(defaultSubscriberBucket);
// Now setup a stats bucket for a different, non-default subscription / subscriber ID.
final long nonDefaultSubRx = 7654321L;
@@ -137,25 +142,21 @@ public class DataUsageControllerTest {
when(nonDefaultSubscriberBucket.getRxBytes()).thenReturn(nonDefaultSubRx);
when(nonDefaultSubscriberBucket.getTxBytes()).thenReturn(nonDefaultSubTx);
final int explicitSubscriptionId = 55;
- final String subscriberId2 = "Test Subscriber 2";
- when(mNetworkStatsManager.querySummaryForDevice(eq(ConnectivityManager.TYPE_MOBILE),
- eq(subscriberId2), eq(0L)/* startTime */, anyLong() /* endTime */)).thenReturn(
+ when(mNetworkStatsManager.querySummaryForDevice(eq(mNetworkTemplate2),
+ eq(0L)/* startTime */, anyLong() /* endTime */)).thenReturn(
nonDefaultSubscriberBucket);
- doReturn(subscriberId2).when(mTelephonyManager).getSubscriberId();
+ doReturn(SUB_ID_2).when(mTelephonyManager).getSubscriberId();
// Now verify that when we're asking for stats on the non-default subscription, we get
// the data back for that subscription and *not* the default one.
mController.setSubscriptionId(explicitSubscriptionId);
- assertThat(mController.getHistoricalUsageLevel(
- NetworkTemplate.buildTemplateMobileAll(subscriberId2))).isEqualTo(
+ assertThat(mController.getHistoricalUsageLevel(mNetworkTemplate2)).isEqualTo(
nonDefaultSubRx + nonDefaultSubTx);
-
- verify(mTelephonyManager).createForSubscriptionId(explicitSubscriptionId);
}
@Test
- public void getTelephonyManager_shouldCreateWithExplicitSubId() throws Exception {
+ public void getTelephonyManager_shouldCreateWithExplicitSubId() {
int explicitSubId = 1;
TelephonyManager tmForSub1 = new TelephonyManager(mContext, explicitSubId);
when(mTelephonyManager.createForSubscriptionId(eq(explicitSubId))).thenReturn(tmForSub1);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
index 011f234ab4f1..c3e161320a1f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
@@ -21,9 +21,9 @@ import static org.mockito.Mockito.when;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
-import android.net.ConnectivityManager;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
+import android.net.NetworkTemplate;
import android.os.RemoteException;
import android.text.format.DateUtils;
@@ -43,6 +43,8 @@ public class NetworkCycleChartDataLoaderTest {
private NetworkPolicyManager mNetworkPolicyManager;
@Mock
private Context mContext;
+ @Mock
+ private NetworkTemplate mNetworkTemplate;
private NetworkCycleChartDataLoader mLoader;
@@ -60,13 +62,12 @@ public class NetworkCycleChartDataLoaderTest {
public void recordUsage_shouldQueryNetworkSummaryForDevice() throws RemoteException {
final long end = System.currentTimeMillis();
final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
- final int networkType = ConnectivityManager.TYPE_MOBILE;
- final String subId = "TestSubscriber";
mLoader = NetworkCycleChartDataLoader.builder(mContext)
- .setSubscriberId(subId).build();
+ .setNetworkTemplate(mNetworkTemplate)
+ .build();
mLoader.recordUsage(start, end);
- verify(mNetworkStatsManager).querySummaryForDevice(networkType, subId, start, end);
+ verify(mNetworkStatsManager).querySummaryForDevice(mNetworkTemplate, start, end);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
index aafb46a1dbf1..877eb615b196 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
@@ -28,9 +28,9 @@ import static org.mockito.Mockito.when;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
-import android.net.ConnectivityManager;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
+import android.net.NetworkTemplate;
import android.text.format.DateUtils;
import org.junit.Before;
@@ -42,6 +42,7 @@ import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class NetworkCycleDataForUidLoaderTest {
+ private static final String SUB_ID = "Test Subscriber";
@Mock
private NetworkStatsManager mNetworkStatsManager;
@@ -49,6 +50,7 @@ public class NetworkCycleDataForUidLoaderTest {
private NetworkPolicyManager mNetworkPolicyManager;
@Mock
private Context mContext;
+ private NetworkTemplate mNetworkTemplate;
private NetworkCycleDataForUidLoader mLoader;
@@ -56,64 +58,62 @@ public class NetworkCycleDataForUidLoaderTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
- .thenReturn(mNetworkStatsManager);
+ .thenReturn(mNetworkStatsManager);
when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
- .thenReturn(mNetworkPolicyManager);
+ .thenReturn(mNetworkPolicyManager);
when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
+ mNetworkTemplate = NetworkTemplate.buildTemplateMobileAll(SUB_ID);
}
@Test
public void recordUsage_shouldQueryNetworkDetailsForUidAndForegroundState() {
final long end = System.currentTimeMillis();
final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
- final int networkType = ConnectivityManager.TYPE_MOBILE;
- final String subId = "TestSubscriber";
final int uid = 1;
mLoader = spy(NetworkCycleDataForUidLoader.builder(mContext)
- .addUid(uid).setSubscriberId(subId).build());
+ .addUid(uid)
+ .setNetworkTemplate(mNetworkTemplate)
+ .build());
doReturn(1024L).when(mLoader).getTotalUsage(any());
mLoader.recordUsage(start, end);
- verify(mNetworkStatsManager).queryDetailsForUid(networkType, subId, start, end, uid);
+ verify(mNetworkStatsManager).queryDetailsForUid(mNetworkTemplate, start, end, uid);
verify(mNetworkStatsManager).queryDetailsForUidTagState(
- networkType, subId, start, end, uid, TAG_NONE, STATE_FOREGROUND);
+ mNetworkTemplate, start, end, uid, TAG_NONE, STATE_FOREGROUND);
}
@Test
public void recordUsage_retrieveDetailIsFalse_shouldNotQueryNetworkForegroundState() {
final long end = System.currentTimeMillis();
final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
- final int networkType = ConnectivityManager.TYPE_MOBILE;
- final String subId = "TestSubscriber";
final int uid = 1;
mLoader = spy(NetworkCycleDataForUidLoader.builder(mContext)
- .setRetrieveDetail(false).addUid(uid).setSubscriberId(subId).build());
+ .setRetrieveDetail(false).addUid(uid).build());
doReturn(1024L).when(mLoader).getTotalUsage(any());
mLoader.recordUsage(start, end);
verify(mNetworkStatsManager, never()).queryDetailsForUidTagState(
- networkType, subId, start, end, uid, TAG_NONE, STATE_FOREGROUND);
+ mNetworkTemplate, start, end, uid, TAG_NONE, STATE_FOREGROUND);
}
@Test
public void recordUsage_multipleUids_shouldQueryNetworkDetailsForEachUid() {
final long end = System.currentTimeMillis();
final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
- final int networkType = ConnectivityManager.TYPE_MOBILE;
- final String subId = "TestSubscriber";
mLoader = spy(NetworkCycleDataForUidLoader.builder(mContext)
- .addUid(1)
- .addUid(2)
- .addUid(3)
- .setSubscriberId(subId).build());
+ .addUid(1)
+ .addUid(2)
+ .addUid(3)
+ .setNetworkTemplate(mNetworkTemplate)
+ .build());
doReturn(1024L).when(mLoader).getTotalUsage(any());
mLoader.recordUsage(start, end);
- verify(mNetworkStatsManager).queryDetailsForUid(networkType, subId, start, end, 1);
- verify(mNetworkStatsManager).queryDetailsForUid(networkType, subId, start, end, 2);
- verify(mNetworkStatsManager).queryDetailsForUid(networkType, subId, start, end, 3);
+ verify(mNetworkStatsManager).queryDetailsForUid(mNetworkTemplate, start, end, 1);
+ verify(mNetworkStatsManager).queryDetailsForUid(mNetworkTemplate, start, end, 2);
+ verify(mNetworkStatsManager).queryDetailsForUid(mNetworkTemplate, start, end, 3);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
index c5f54bb0f0d9..74b91510cf3f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -126,8 +126,6 @@ public class NetworkCycleDataLoaderTest {
when(mIterator.next()).thenReturn(cycle);
mLoader = spy(new NetworkCycleDataTestLoader(mContext));
ReflectionHelpers.setField(mLoader, "mPolicy", mPolicy);
- ReflectionHelpers.setField(mLoader, "mNetworkType", networkType);
- ReflectionHelpers.setField(mLoader, "mSubId", subId);
mLoader.loadPolicyData();
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 715e1ebe31ac..dd72d5779c19 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -225,7 +225,7 @@
<bool name="def_charging_sounds_enabled">true</bool>
<!-- Default for Settings.Secure.NOTIFICATION_BUBBLES -->
- <bool name="def_notification_bubbles">true</bool>
+ <bool name="def_notification_bubbles">false</bool>
<!-- Default for Settings.Secure.AWARE_ENABLED -->
<bool name="def_aware_enabled">false</bool>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 82592ceeb710..6558c87aaf3a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3237,7 +3237,7 @@ public class SettingsProvider extends ContentProvider {
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 179;
+ private static final int SETTINGS_VERSION = 180;
private final int mUserId;
@@ -4387,6 +4387,19 @@ public class SettingsProvider extends ContentProvider {
currentVersion = 179;
}
+ if (currentVersion == 179) {
+ // Version 178: Reset the default for Secure Settings: NOTIFICATION_BUBBLES
+ // This is originally set in version 173, however, the default value changed
+ // so this step is to ensure the value is updated to the correct defaulte
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+
+ secureSettings.insertSettingLocked(Secure.NOTIFICATION_BUBBLES,
+ getContext().getResources().getBoolean(
+ R.bool.def_notification_bubbles) ? "1" : "0", null,
+ true, SettingsState.SYSTEM_PACKAGE_NAME);
+
+ currentVersion = 180;
+ }
// vXXX: Add new settings above this point.
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index c2495b586144..94259416d274 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -58,6 +58,7 @@ android_library {
"androidx.arch.core_core-runtime",
"androidx.lifecycle_lifecycle-extensions",
"androidx.dynamicanimation_dynamicanimation",
+ "iconloader_base",
"SystemUI-tags",
"SystemUI-proto",
"dagger2-2.19",
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/LegacyRecentsImpl.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/LegacyRecentsImpl.java
index b7bb751c1582..a150de95fcf0 100644
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/LegacyRecentsImpl.java
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/LegacyRecentsImpl.java
@@ -662,11 +662,17 @@ public class LegacyRecentsImpl implements RecentsImplementation {
public final void onBusEvent(ExpandPipEvent event) {
PipUI pipUi = getComponent(PipUI.class);
+ if (pipUi == null) {
+ return;
+ }
pipUi.expandPip();
}
public final void onBusEvent(HidePipMenuEvent event) {
PipUI pipUi = getComponent(PipUI.class);
+ if (pipUi == null) {
+ return;
+ }
event.getAnimationTrigger().increment();
pipUi.hidePipMenu(() -> {
event.getAnimationTrigger().increment();
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
index 29376ce01e4f..796123db7c79 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
@@ -38,6 +38,7 @@
android:clipChildren="false"
android:clipToPadding="false"
android:padding="0dp"
+ android:fitsSystemWindows="true"
android:layout_gravity="center">
<com.android.keyguard.KeyguardSecurityViewFlipper
android:id="@+id/view_flipper"
diff --git a/packages/SystemUI/res/drawable/bubble_flyout.xml b/packages/SystemUI/res/drawable/bubble_flyout.xml
deleted file mode 100644
index afe5372d38d8..000000000000
--- a/packages/SystemUI/res/drawable/bubble_flyout.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
- <item>
- <shape android:shape="rectangle">
- <solid android:color="?android:attr/colorBackgroundFloating" />
- <corners
- android:bottomLeftRadius="?android:attr/dialogCornerRadius"
- android:topLeftRadius="?android:attr/dialogCornerRadius"
- android:bottomRightRadius="?android:attr/dialogCornerRadius"
- android:topRightRadius="?android:attr/dialogCornerRadius" />
- <padding
- android:left="@dimen/bubble_flyout_pointer_size"
- android:right="@dimen/bubble_flyout_pointer_size" />
- </shape>
- </item>
-</layer-list> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/biometric_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml
index 1abb8735ddab..c560d7e8f126 100644
--- a/packages/SystemUI/res/layout/biometric_dialog.xml
+++ b/packages/SystemUI/res/layout/biometric_dialog.xml
@@ -37,7 +37,8 @@
android:id="@+id/space"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_weight="1" />
+ android:layout_weight="1"
+ android:contentDescription="@string/biometric_dialog_empty_space_description"/>
<ScrollView
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/bubble_flyout.xml b/packages/SystemUI/res/layout/bubble_flyout.xml
index 0e4d2985e775..5f773f462deb 100644
--- a/packages/SystemUI/res/layout/bubble_flyout.xml
+++ b/packages/SystemUI/res/layout/bubble_flyout.xml
@@ -13,18 +13,13 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="@dimen/bubble_flyout_pointer_size"
- android:paddingRight="@dimen/bubble_flyout_pointer_size">
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
<FrameLayout
- android:id="@+id/bubble_flyout"
+ android:id="@+id/bubble_flyout_text_container"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- android:background="@drawable/bubble_flyout"
+ android:clipToPadding="false"
android:paddingLeft="@dimen/bubble_flyout_padding_x"
android:paddingRight="@dimen/bubble_flyout_padding_x"
android:paddingTop="@dimen/bubble_flyout_padding_y"
@@ -41,4 +36,4 @@
</FrameLayout>
-</FrameLayout> \ No newline at end of file
+</merge> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index cdef09d73f77..2792a019f8d8 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -31,23 +31,36 @@
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/keyguard_indication_margin_bottom"
android:layout_gravity="bottom|center_horizontal"
- android:orientation="vertical">
+ android:orientation="horizontal">
- <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
- android:id="@+id/keyguard_indication_enterprise_disclosure"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
- android:visibility="gone" />
+ <include layout="@layout/left_docked_overlay" />
- <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
- android:id="@+id/keyguard_indication_text"
- android:layout_width="match_parent"
+ <LinearLayout
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="center"
- android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
- android:accessibilityLiveRegion="polite" />
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical|center_horizontal"
+ android:orientation="vertical">
+
+ <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
+ android:id="@+id/keyguard_indication_enterprise_disclosure"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
+ android:visibility="gone" />
+
+ <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
+ android:id="@+id/keyguard_indication_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
+ android:accessibilityLiveRegion="polite" />
+
+ </LinearLayout>
+
+ <include layout="@layout/right_docked_overlay" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/left_docked_overlay.xml b/packages/SystemUI/res/layout/left_docked_overlay.xml
new file mode 100644
index 000000000000..430143ca3bc2
--- /dev/null
+++ b/packages/SystemUI/res/layout/left_docked_overlay.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- empty stub -->
+<merge />
diff --git a/packages/SystemUI/res/layout/right_docked_overlay.xml b/packages/SystemUI/res/layout/right_docked_overlay.xml
new file mode 100644
index 000000000000..430143ca3bc2
--- /dev/null
+++ b/packages/SystemUI/res/layout/right_docked_overlay.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- empty stub -->
+<merge />
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 4cf5f850285e..a91493003bb5 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -69,7 +69,7 @@
android:layout_height="match_parent"
android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
- />
+ />
<LinearLayout
android:id="@+id/lock_icon_container"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index fbb439af7c51..62974238cd9f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -252,6 +252,9 @@
<!-- size at which Notification icons will be drawn on Ambient Display -->
<dimen name="status_bar_icon_drawing_size_dark">@*android:dimen/notification_header_icon_size_ambient</dimen>
+ <!-- size of notification icons on AOD -->
+ <dimen name="dark_shelf_icon_size">16dp</dimen>
+
<!-- opacity at which Notification icons will be drawn in the status bar -->
<item type="dimen" name="status_bar_icon_drawing_alpha">90%</item>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 26432210e233..e01e6a84e9d5 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -288,8 +288,18 @@
<!-- Message shown when a biometric is authenticated, asking the user to confirm authentication [CHAR LIMIT=30] -->
<string name="biometric_dialog_confirm">Confirm</string>
- <!-- Button name on BiometricPrompt shown when a biometric is detected but not authenticated. Tapping the button resumes authentication [CHAR_LIMIT=30] -->
+ <!-- Button name on BiometricPrompt shown when a biometric is detected but not authenticated. Tapping the button resumes authentication [CHAR LIMIT=30] -->
<string name="biometric_dialog_try_again">Try again</string>
+ <!-- Content description for empty spaces that are not taken by the biometric dialog. Clicking on these areas will cancel authentication and dismiss the biometric dialog [CHAR LIMIT=NONE] -->
+ <string name="biometric_dialog_empty_space_description">Empty region, tap to cancel authentication</string>
+ <!-- Content description for the face icon when the device is not authenticating anymore [CHAR LIMIT=NONE] -->
+ <string name="biometric_dialog_face_icon_description_idle">Please try again</string>
+ <!-- Content description for the face icon when the device is authenticating [CHAR LIMIT=NONE] -->
+ <string name="biometric_dialog_face_icon_description_authenticating">Looking for your face</string>
+ <!-- Content description for the face icon when the user has been authenticated [CHAR LIMIT=NONE] -->
+ <string name="biometric_dialog_face_icon_description_authenticated">Face authenticated</string>
+ <!-- Content description for the face icon when the user has been authenticated and the confirm button has been pressed [CHAR LIMIT=NONE] -->
+ <string name="biometric_dialog_face_icon_description_confirmed">Confirmed</string>
<!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication -->
<string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 670980420a57..577e3bbefdad 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -134,9 +134,4 @@ oneway interface IOverviewProxy {
* Sent when some system ui state changes.
*/
void onSystemUiStateChanged(int stateFlags) = 16;
-
- /**
- * Sent when the scrim colors (based on wallpaper) change.
- */
- void onScrimColorsChanged(int color, int type) = 17;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index d051defc1f25..0914fb8330be 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -20,6 +20,7 @@ import android.app.AlertDialog;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.graphics.Rect;
import android.metrics.LogMaker;
import android.os.UserHandle;
import android.util.AttributeSet;
@@ -139,7 +140,6 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
getSecurityView(mCurrentSecuritySelection).onResume(reason);
}
updateBiometricRetry();
- updatePaddings();
}
@Override
@@ -180,7 +180,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
}
int index = event.findPointerIndex(mActivePointerId);
int touchSlop = mViewConfiguration.getScaledTouchSlop();
- if (mCurrentSecurityView != null
+ if (mCurrentSecurityView != null && index != -1
&& mStartTouchY - event.getY(index) > touchSlop) {
mIsDragging = true;
return true;
@@ -319,17 +319,11 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
}
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- updatePaddings();
- }
-
- private void updatePaddings() {
- int bottomPadding = getRootWindowInsets().getSystemWindowInsets().bottom;
- if (getPaddingBottom() == bottomPadding) {
- return;
- }
- setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), bottomPadding);
+ protected boolean fitSystemWindows(Rect insets) {
+ // Consume bottom insets because we're setting the padding locally (for IME and navbar.)
+ setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), insets.bottom);
+ insets.bottom = 0;
+ return false;
}
private void showDialog(String title, String message) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index dd6ccb2b3a88..ea8565e8d301 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -238,6 +238,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
private boolean mIsDreaming;
private final DevicePolicyManager mDevicePolicyManager;
private boolean mLogoutEnabled;
+ // If the user long pressed the lock icon, disabling face auth for the current session.
+ private boolean mLockIconPressed;
/**
* Short delay before restarting biometric authentication after a successful try
@@ -1384,6 +1386,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
}
private void handleScreenTurnedOff() {
+ mLockIconPressed = false;
mHardwareFingerprintUnavailableRetryCount = 0;
mHardwareFaceUnavailableRetryCount = 0;
final int count = mCallbacks.size();
@@ -1625,10 +1628,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
return (mBouncer || mAuthInterruptActive || awakeKeyguard || shouldListenForFaceAssistant())
&& !mSwitchingUser && !getUserCanSkipBouncer(user) && !isFaceDisabled(user)
- && !mKeyguardGoingAway && mFaceSettingEnabledForUser
+ && !mKeyguardGoingAway && mFaceSettingEnabledForUser && !mLockIconPressed
&& mUserManager.isUserUnlocked(user) && mIsPrimaryUser;
}
+ /**
+ * Whenever the lock icon is long pressed, disabling trust agents.
+ * This means that we cannot auth passively (face) until the user presses power.
+ */
+ public void onLockIconPressed() {
+ mLockIconPressed = true;
+ mUserFaceAuthenticated.put(getCurrentUser(), false);
+ updateFaceListeningState();
+ }
private void startListeningForFingerprint() {
if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index 9dfcf7d01e31..45c19addd1de 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -325,7 +325,6 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba
private void handleTryAgainPressed() {
try {
- mCurrentDialog.clearTemporaryMessage();
mReceiver.onTryAgainPressed();
} catch (RemoteException e) {
Log.e(TAG, "RemoteException when handling try again", e);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index f99587b6cdc2..5717a54fd8a0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -33,7 +33,6 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -224,13 +223,11 @@ public abstract class BiometricDialogView extends LinearLayout {
});
mTryAgainButton.setOnClickListener((View v) -> {
+ handleResetMessage();
updateState(STATE_AUTHENTICATING);
showTryAgainButton(false /* show */);
mCallback.onTryAgainPressed();
});
-
- mLayout.setFocusableInTouchMode(true);
- mLayout.requestFocus();
}
public void onSaveState(Bundle bundle) {
@@ -269,6 +266,7 @@ public abstract class BiometricDialogView extends LinearLayout {
if (mRestoredState == null) {
updateState(STATE_AUTHENTICATING);
mErrorText.setText(getHintStringResourceId());
+ mErrorText.setContentDescription(mContext.getString(getHintStringResourceId()));
mErrorText.setVisibility(View.VISIBLE);
} else {
updateState(mState);
@@ -278,7 +276,6 @@ public abstract class BiometricDialogView extends LinearLayout {
mTitleText.setVisibility(View.VISIBLE);
mTitleText.setText(titleText);
- mTitleText.setSelected(true);
final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
if (TextUtils.isEmpty(subtitleText)) {
@@ -323,11 +320,10 @@ public abstract class BiometricDialogView extends LinearLayout {
private void setDismissesDialog(View v) {
v.setClickable(true);
- v.setOnTouchListener((View view, MotionEvent event) -> {
+ v.setOnClickListener(v1 -> {
if (mState != STATE_AUTHENTICATED && shouldGrayAreaDismissDialog()) {
mCallback.onUserCanceled();
}
- return true;
});
}
@@ -421,11 +417,6 @@ public abstract class BiometricDialogView extends LinearLayout {
BiometricPrompt.HIDE_DIALOG_DELAY);
}
- public void clearTemporaryMessage() {
- mHandler.removeMessages(MSG_RESET_MESSAGE);
- mHandler.obtainMessage(MSG_RESET_MESSAGE).sendToTarget();
- }
-
/**
* Transient help message (acquire) is received, dialog stays showing. Sensor stays in
* "authenticating" state.
@@ -484,6 +475,7 @@ public abstract class BiometricDialogView extends LinearLayout {
mPositiveButton.setVisibility(bundle.getInt(KEY_CONFIRM_VISIBILITY));
mState = bundle.getInt(KEY_STATE);
mErrorText.setText(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
+ mErrorText.setContentDescription(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
mErrorText.setVisibility(bundle.getInt(KEY_ERROR_TEXT_VISIBILITY));
mErrorText.setTextColor(bundle.getInt(KEY_ERROR_TEXT_COLOR));
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
index dbbb71c93c03..8f26f1847779 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
@@ -288,6 +288,7 @@ public class FaceDialogView extends BiometricDialogView {
@Override
protected void handleResetMessage() {
mErrorText.setText(getHintStringResourceId());
+ mErrorText.setContentDescription(mContext.getString(getHintStringResourceId()));
mErrorText.setTextColor(mTextColor);
if (getState() == STATE_AUTHENTICATING) {
mErrorText.setVisibility(View.VISIBLE);
@@ -406,13 +407,21 @@ public class FaceDialogView extends BiometricDialogView {
} else {
mIconController.showIcon(R.drawable.face_dialog_pulse_dark_to_light);
}
+ mBiometricIcon.setContentDescription(mContext.getString(
+ R.string.biometric_dialog_face_icon_description_authenticating));
} else if (oldState == STATE_PENDING_CONFIRMATION && newState == STATE_AUTHENTICATED) {
mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark);
+ mBiometricIcon.setContentDescription(mContext.getString(
+ R.string.biometric_dialog_face_icon_description_confirmed));
} else if (oldState == STATE_ERROR && newState == STATE_IDLE) {
mIconController.animateOnce(R.drawable.face_dialog_error_to_idle);
+ mBiometricIcon.setContentDescription(mContext.getString(
+ R.string.biometric_dialog_face_icon_description_idle));
} else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATED) {
mHandler.removeCallbacks(mErrorToIdleAnimationRunnable);
mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark);
+ mBiometricIcon.setContentDescription(mContext.getString(
+ R.string.biometric_dialog_face_icon_description_authenticated));
} else if (newState == STATE_ERROR) {
// It's easier to only check newState and gate showing the animation on the
// mErrorToIdleAnimationRunnable as a proxy, than add a ton of extra state. For example,
@@ -426,11 +435,17 @@ public class FaceDialogView extends BiometricDialogView {
}
} else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark);
+ mBiometricIcon.setContentDescription(mContext.getString(
+ R.string.biometric_dialog_face_icon_description_authenticated));
} else if (newState == STATE_PENDING_CONFIRMATION) {
mHandler.removeCallbacks(mErrorToIdleAnimationRunnable);
mIconController.animateOnce(R.drawable.face_dialog_wink_from_dark);
+ mBiometricIcon.setContentDescription(mContext.getString(
+ R.string.biometric_dialog_face_icon_description_authenticated));
} else if (newState == STATE_IDLE) {
mIconController.showStatic(R.drawable.face_dialog_idle_static);
+ mBiometricIcon.setContentDescription(mContext.getString(
+ R.string.biometric_dialog_face_icon_description_idle));
} else {
Log.w(TAG, "Unknown animation from " + oldState + " -> " + newState);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgeRenderer.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgeRenderer.java
index 845b08483064..74ad0faca6d3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgeRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgeRenderer.java
@@ -18,12 +18,15 @@ package com.android.systemui.bubbles;
import static android.graphics.Paint.ANTI_ALIAS_FLAG;
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Log;
+import com.android.systemui.R;
+
// XXX: Mostly opied from launcher code / can we share?
/**
* Contains parameters necessary to draw a badge for an icon (e.g. the size of the badge).
@@ -32,20 +35,31 @@ public class BadgeRenderer {
private static final String TAG = "BadgeRenderer";
- // The badge sizes are defined as percentages of the app icon size.
+ /** The badge sizes are defined as percentages of the app icon size. */
private static final float SIZE_PERCENTAGE = 0.38f;
- // Extra scale down of the dot
+ /** Extra scale down of the dot. */
private static final float DOT_SCALE = 0.6f;
private final float mDotCenterOffset;
private final float mCircleRadius;
private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG);
- public BadgeRenderer(int iconSizePx) {
- mDotCenterOffset = SIZE_PERCENTAGE * iconSizePx;
- int size = (int) (DOT_SCALE * mDotCenterOffset);
- mCircleRadius = size / 2f;
+ public BadgeRenderer(Context context) {
+ mDotCenterOffset = getDotCenterOffset(context);
+ mCircleRadius = getDotRadius(mDotCenterOffset);
+ }
+
+ /** Space between the center of the dot and the top or left of the bubble stack. */
+ static float getDotCenterOffset(Context context) {
+ final int iconSizePx =
+ context.getResources().getDimensionPixelSize(R.dimen.individual_bubble_size);
+ return SIZE_PERCENTAGE * iconSizePx;
+ }
+
+ static float getDotRadius(float dotCenterOffset) {
+ int size = (int) (DOT_SCALE * dotCenterOffset);
+ return size / 2f;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
index f15e8e47649c..783780f8819c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
@@ -57,7 +57,7 @@ public class BadgedImageView extends ImageView {
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mIconSize = getResources().getDimensionPixelSize(R.dimen.individual_bubble_size);
- mDotRenderer = new BadgeRenderer(mIconSize);
+ mDotRenderer = new BadgeRenderer(getContext());
TypedArray ta = context.obtainStyledAttributes(
new int[] {android.R.attr.colorBackgroundFloating});
@@ -83,6 +83,10 @@ public class BadgedImageView extends ImageView {
invalidate();
}
+ public boolean getDotPosition() {
+ return mOnLeft;
+ }
+
/**
* Set whether the dot should show or not.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index ac4a93ba7fb0..8aad0f8bd831 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -83,7 +83,7 @@ class Bubble {
public void updateDotVisibility() {
if (iconView != null) {
- iconView.updateDotVisibility();
+ iconView.updateDotVisibility(true /* animate */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
new file mode 100644
index 000000000000..71f68c16bd8d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import static android.graphics.Paint.ANTI_ALIAS_FLAG;
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+
+import android.animation.ArgbEvaluator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.graphics.RectF;
+import android.graphics.drawable.ShapeDrawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.SpringAnimation;
+
+import com.android.systemui.R;
+import com.android.systemui.recents.TriangleShape;
+
+/**
+ * Flyout view that appears as a 'chat bubble' alongside the bubble stack. The flyout can visually
+ * transform into the 'new' dot, which is used during flyout dismiss animations/gestures.
+ */
+public class BubbleFlyoutView extends FrameLayout {
+ /** Max width of the flyout, in terms of percent of the screen width. */
+ private static final float FLYOUT_MAX_WIDTH_PERCENT = .6f;
+
+ private final int mFlyoutPadding;
+ private final int mFlyoutSpaceFromBubble;
+ private final int mPointerSize;
+ private final int mBubbleSize;
+ private final int mFlyoutElevation;
+ private final int mBubbleElevation;
+ private final int mFloatingBackgroundColor;
+ private final float mCornerRadius;
+
+ private final ViewGroup mFlyoutTextContainer;
+ private final TextView mFlyoutText;
+ /** Spring animation for the flyout. */
+ private final SpringAnimation mFlyoutSpring =
+ new SpringAnimation(this, DynamicAnimation.TRANSLATION_X);
+
+ /** Values related to the 'new' dot which we use to figure out where to collapse the flyout. */
+ private final float mNewDotRadius;
+ private final float mNewDotSize;
+ private final float mNewDotOffsetFromBubbleBounds;
+
+ /**
+ * The paint used to draw the background, whose color changes as the flyout transitions to the
+ * tinted 'new' dot.
+ */
+ private final Paint mBgPaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG);
+ private final ArgbEvaluator mArgbEvaluator = new ArgbEvaluator();
+
+ /**
+ * Triangular ShapeDrawables used for the triangle that points from the flyout to the bubble
+ * stack (a chat-bubble effect).
+ */
+ private final ShapeDrawable mLeftTriangleShape;
+ private final ShapeDrawable mRightTriangleShape;
+
+ /** Whether the flyout arrow is on the left (pointing left) or right (pointing right). */
+ private boolean mArrowPointingLeft = true;
+
+ /** Color of the 'new' dot that the flyout will transform into. */
+ private int mDotColor;
+
+ /** The outline of the triangle, used for elevation shadows. */
+ private final Outline mTriangleOutline = new Outline();
+
+ /** The bounds of the flyout background, kept up to date as it transitions to the 'new' dot. */
+ private final RectF mBgRect = new RectF();
+
+ /**
+ * Percent progress in the transition from flyout to 'new' dot. These two values are the inverse
+ * of each other (if we're 40% transitioned to the dot, we're 60% flyout), but it makes the code
+ * much more readable.
+ */
+ private float mPercentTransitionedToDot = 1f;
+ private float mPercentStillFlyout = 0f;
+
+ /**
+ * The difference in values between the flyout and the dot. These differences are gradually
+ * added over the course of the animation to transform the flyout into the 'new' dot.
+ */
+ private float mFlyoutToDotWidthDelta = 0f;
+ private float mFlyoutToDotHeightDelta = 0f;
+ private float mFlyoutToDotCornerRadiusDelta;
+
+ /** The translation values when the flyout is completely transitioned into the dot. */
+ private float mTranslationXWhenDot = 0f;
+ private float mTranslationYWhenDot = 0f;
+
+ /**
+ * The current translation values applied to the flyout background as it transitions into the
+ * 'new' dot.
+ */
+ private float mBgTranslationX;
+ private float mBgTranslationY;
+
+ /** The flyout's X translation when at rest (not animating or dragging). */
+ private float mRestingTranslationX = 0f;
+
+ /** Callback to run when the flyout is hidden. */
+ private Runnable mOnHide;
+
+ public BubbleFlyoutView(Context context) {
+ super(context);
+ LayoutInflater.from(context).inflate(R.layout.bubble_flyout, this, true);
+
+ mFlyoutTextContainer = findViewById(R.id.bubble_flyout_text_container);
+ mFlyoutText = mFlyoutTextContainer.findViewById(R.id.bubble_flyout_text);
+
+ final Resources res = getResources();
+ mFlyoutPadding = res.getDimensionPixelSize(R.dimen.bubble_flyout_padding_x);
+ mFlyoutSpaceFromBubble = res.getDimensionPixelSize(R.dimen.bubble_flyout_space_from_bubble);
+ mPointerSize = res.getDimensionPixelSize(R.dimen.bubble_flyout_pointer_size);
+ mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
+ mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
+ mFlyoutElevation = res.getDimensionPixelSize(R.dimen.bubble_flyout_elevation);
+ mNewDotOffsetFromBubbleBounds = BadgeRenderer.getDotCenterOffset(context);
+ mNewDotRadius = BadgeRenderer.getDotRadius(mNewDotOffsetFromBubbleBounds);
+ mNewDotSize = mNewDotRadius * 2f;
+
+ final TypedArray ta = mContext.obtainStyledAttributes(
+ new int[] {
+ android.R.attr.colorBackgroundFloating,
+ android.R.attr.dialogCornerRadius});
+ mFloatingBackgroundColor = ta.getColor(0, Color.WHITE);
+ mCornerRadius = ta.getDimensionPixelSize(1, 0);
+ mFlyoutToDotCornerRadiusDelta = mNewDotRadius - mCornerRadius;
+ ta.recycle();
+
+ // Add padding for the pointer on either side, onDraw will draw it in this space.
+ setPadding(mPointerSize, 0, mPointerSize, 0);
+ setWillNotDraw(false);
+ setClipChildren(false);
+ setTranslationZ(mFlyoutElevation);
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ BubbleFlyoutView.this.getOutline(outline);
+ }
+ });
+
+ mBgPaint.setColor(mFloatingBackgroundColor);
+
+ mLeftTriangleShape =
+ new ShapeDrawable(TriangleShape.createHorizontal(
+ mPointerSize, mPointerSize, true /* isPointingLeft */));
+ mLeftTriangleShape.setBounds(0, 0, mPointerSize, mPointerSize);
+ mLeftTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
+
+ mRightTriangleShape =
+ new ShapeDrawable(TriangleShape.createHorizontal(
+ mPointerSize, mPointerSize, false /* isPointingLeft */));
+ mRightTriangleShape.setBounds(0, 0, mPointerSize, mPointerSize);
+ mRightTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ renderBackground(canvas);
+ invalidateOutline();
+ super.onDraw(canvas);
+ }
+
+ /** Configures the flyout and animates it in. */
+ void showFlyout(
+ CharSequence updateMessage, PointF stackPos, float parentWidth,
+ boolean arrowPointingLeft, int dotColor, Runnable onHide) {
+ mArrowPointingLeft = arrowPointingLeft;
+ mDotColor = dotColor;
+ mOnHide = onHide;
+
+ setCollapsePercent(0f);
+ setAlpha(0f);
+ setVisibility(VISIBLE);
+
+ // Set the flyout TextView's max width in terms of percent, and then subtract out the
+ // padding so that the entire flyout view will be the desired width (rather than the
+ // TextView being the desired width + extra padding).
+ mFlyoutText.setMaxWidth(
+ (int) (parentWidth * FLYOUT_MAX_WIDTH_PERCENT) - mFlyoutPadding * 2);
+ mFlyoutText.setText(updateMessage);
+
+ // Wait for the TextView to lay out so we know its line count.
+ post(() -> {
+ // Multi line flyouts get top-aligned to the bubble.
+ if (mFlyoutText.getLineCount() > 1) {
+ setTranslationY(stackPos.y);
+ } else {
+ // Single line flyouts are vertically centered with respect to the bubble.
+ setTranslationY(
+ stackPos.y + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f);
+ }
+
+ // Calculate the translation required to position the flyout next to the bubble stack,
+ // with the desired padding.
+ mRestingTranslationX = mArrowPointingLeft
+ ? stackPos.x + mBubbleSize + mFlyoutSpaceFromBubble
+ : stackPos.x - getWidth() - mFlyoutSpaceFromBubble;
+
+ // Translate towards the stack slightly.
+ setTranslationX(
+ mRestingTranslationX + (arrowPointingLeft ? -mBubbleSize : mBubbleSize));
+
+ // Fade in the entire flyout and spring it to its normal position.
+ animate().alpha(1f);
+ mFlyoutSpring.animateToFinalPosition(mRestingTranslationX);
+
+ // Calculate the difference in size between the flyout and the 'dot' so that we can
+ // transform into the dot later.
+ mFlyoutToDotWidthDelta = getWidth() - mNewDotSize;
+ mFlyoutToDotHeightDelta = getHeight() - mNewDotSize;
+
+ // Calculate the translation values needed to be in the correct 'new dot' position.
+ final float distanceFromFlyoutLeftToDotCenterX =
+ mFlyoutSpaceFromBubble + mNewDotOffsetFromBubbleBounds / 2;
+ if (mArrowPointingLeft) {
+ mTranslationXWhenDot = -distanceFromFlyoutLeftToDotCenterX - mNewDotRadius;
+ } else {
+ mTranslationXWhenDot =
+ getWidth() + distanceFromFlyoutLeftToDotCenterX - mNewDotRadius;
+ }
+
+ mTranslationYWhenDot =
+ getHeight() / 2f
+ - mNewDotRadius
+ - mBubbleSize / 2f
+ + mNewDotOffsetFromBubbleBounds / 2;
+ });
+ }
+
+ /**
+ * Hides the flyout and runs the optional callback passed into showFlyout. The flyout has been
+ * animated into the 'new' dot by the time we call this, so no animations are needed.
+ */
+ void hideFlyout() {
+ if (mOnHide != null) {
+ mOnHide.run();
+ mOnHide = null;
+ }
+
+ setVisibility(GONE);
+ }
+
+ /** Sets the percentage that the flyout should be collapsed into dot form. */
+ void setCollapsePercent(float percentCollapsed) {
+ mPercentTransitionedToDot = Math.max(0f, Math.min(percentCollapsed, 1f));
+ mPercentStillFlyout = (1f - mPercentTransitionedToDot);
+
+ // Move and fade out the text.
+ mFlyoutText.setTranslationX(
+ (mArrowPointingLeft ? -getWidth() : getWidth()) * mPercentTransitionedToDot);
+ mFlyoutText.setAlpha(clampPercentage(
+ (mPercentStillFlyout - (1f - BubbleStackView.FLYOUT_DRAG_PERCENT_DISMISS))
+ / BubbleStackView.FLYOUT_DRAG_PERCENT_DISMISS));
+
+ // Reduce the elevation towards that of the topmost bubble.
+ setTranslationZ(
+ mFlyoutElevation
+ - (mFlyoutElevation - mBubbleElevation) * mPercentTransitionedToDot);
+ invalidate();
+ }
+
+ /** Return the flyout's resting X translation (translation when not dragging or animating). */
+ float getRestingTranslationX() {
+ return mRestingTranslationX;
+ }
+
+ /** Clamps a float to between 0 and 1. */
+ private float clampPercentage(float percent) {
+ return Math.min(1f, Math.max(0f, percent));
+ }
+
+ /**
+ * Renders the background, which is either the rounded 'chat bubble' flyout, or some state
+ * between that and the 'new' dot over the bubbles.
+ */
+ private void renderBackground(Canvas canvas) {
+ // Calculate the width, height, and corner radius of the flyout given the current collapsed
+ // percentage.
+ final float width = getWidth() - (mFlyoutToDotWidthDelta * mPercentTransitionedToDot);
+ final float height = getHeight() - (mFlyoutToDotHeightDelta * mPercentTransitionedToDot);
+ final float cornerRadius = mCornerRadius
+ - (mFlyoutToDotCornerRadiusDelta * mPercentTransitionedToDot);
+
+ // Translate the flyout background towards the collapsed 'dot' state.
+ mBgTranslationX = mTranslationXWhenDot * mPercentTransitionedToDot;
+ mBgTranslationY = mTranslationYWhenDot * mPercentTransitionedToDot;
+
+ // Set the bounds of the rounded rectangle that serves as either the flyout background or
+ // the collapsed 'dot'. These bounds will also be used to provide the outline for elevation
+ // shadows. In the expanded flyout state, the left and right bounds leave space for the
+ // pointer triangle - as the flyout collapses, this space is reduced since the triangle
+ // retracts into the flyout.
+ mBgRect.set(
+ mPointerSize * mPercentStillFlyout /* left */,
+ 0 /* top */,
+ width - mPointerSize * mPercentStillFlyout /* right */,
+ height /* bottom */);
+
+ mBgPaint.setColor(
+ (int) mArgbEvaluator.evaluate(
+ mPercentTransitionedToDot, mFloatingBackgroundColor, mDotColor));
+
+ canvas.save();
+ canvas.translate(mBgTranslationX, mBgTranslationY);
+ renderPointerTriangle(canvas, width, height);
+ canvas.drawRoundRect(mBgRect, cornerRadius, cornerRadius, mBgPaint);
+ canvas.restore();
+ }
+
+ /** Renders the 'pointer' triangle that points from the flyout to the bubble stack. */
+ private void renderPointerTriangle(
+ Canvas canvas, float currentFlyoutWidth, float currentFlyoutHeight) {
+ canvas.save();
+
+ // Translation to apply for the 'retraction' effect as the flyout collapses.
+ final float retractionTranslationX =
+ (mArrowPointingLeft ? 1 : -1) * (mPercentTransitionedToDot * mPointerSize * 2f);
+
+ // Place the arrow either at the left side, or the far right, depending on whether the
+ // flyout is on the left or right side.
+ final float arrowTranslationX =
+ mArrowPointingLeft
+ ? retractionTranslationX
+ : currentFlyoutWidth - mPointerSize + retractionTranslationX;
+
+ // Vertically center the arrow at all times.
+ final float arrowTranslationY = currentFlyoutHeight / 2f - mPointerSize / 2f;
+
+ // Draw the appropriate direction of arrow.
+ final ShapeDrawable relevantTriangle =
+ mArrowPointingLeft ? mLeftTriangleShape : mRightTriangleShape;
+ canvas.translate(arrowTranslationX, arrowTranslationY);
+ relevantTriangle.setAlpha((int) (255f * mPercentStillFlyout));
+ relevantTriangle.draw(canvas);
+
+ // Save the triangle's outline for use in the outline provider, offsetting it to reflect its
+ // current position.
+ relevantTriangle.getOutline(mTriangleOutline);
+ mTriangleOutline.offset((int) arrowTranslationX, (int) arrowTranslationY);
+
+ canvas.restore();
+ }
+
+ /** Builds an outline that includes the transformed flyout background and triangle. */
+ private void getOutline(Outline outline) {
+ if (!mTriangleOutline.isEmpty()) {
+ // Draw the rect into the outline as a path so we can merge the triangle path into it.
+ final Path rectPath = new Path();
+ rectPath.addRoundRect(mBgRect, mCornerRadius, mCornerRadius, Path.Direction.CW);
+ outline.setConvexPath(rectPath);
+
+ // Get rid of the triangle path once it has disappeared behind the flyout.
+ if (mPercentStillFlyout > 0.5f) {
+ outline.mPath.addPath(mTriangleOutline.mPath);
+ }
+
+ // Translate the outline to match the background's position.
+ final Matrix outlineMatrix = new Matrix();
+ outlineMatrix.postTranslate(getLeft() + mBgTranslationX, getTop() + mBgTranslationY);
+
+ // At the very end, retract the outline into the bubble so the shadow will be pulled
+ // into the flyout-dot as it (visually) becomes part of the bubble. We can't do this by
+ // animating translationZ to zero since then it'll go under the bubbles, which have
+ // elevation.
+ if (mPercentTransitionedToDot > 0.98f) {
+ final float percentBetween99and100 = (mPercentTransitionedToDot - 0.98f) / .02f;
+ final float percentShadowVisible = 1f - percentBetween99and100;
+
+ // Keep it centered.
+ outlineMatrix.postTranslate(
+ mNewDotRadius * percentBetween99and100,
+ mNewDotRadius * percentBetween99and100);
+ outlineMatrix.preScale(percentShadowVisible, percentShadowVisible);
+ }
+
+ outline.mPath.transform(outlineMatrix);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 2b1742592fba..4fef157183c2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -25,8 +25,6 @@ import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Outline;
@@ -35,8 +33,6 @@ import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.ShapeDrawable;
import android.os.Bundle;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -56,11 +52,11 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
-import android.widget.TextView;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
@@ -70,7 +66,6 @@ import com.android.systemui.R;
import com.android.systemui.bubbles.animation.ExpandedAnimationController;
import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
import com.android.systemui.bubbles.animation.StackAnimationController;
-import com.android.systemui.recents.TriangleShape;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.math.BigDecimal;
@@ -86,12 +81,21 @@ public class BubbleStackView extends FrameLayout {
private static final String TAG = "BubbleStackView";
private static final boolean DEBUG = false;
+ /** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */
+ static final float FLYOUT_DRAG_PERCENT_DISMISS = 0.25f;
+
+ /** Velocity required to dismiss the flyout via drag. */
+ private static final float FLYOUT_DISMISS_VELOCITY = 2000f;
+
+ /**
+ * Factor for attenuating translation when the flyout is overscrolled (8f = flyout moves 1 pixel
+ * for every 8 pixels overscrolled).
+ */
+ private static final float FLYOUT_OVERSCROLL_ATTENUATION_FACTOR = 8f;
+
/** Duration of the flyout alpha animations. */
private static final int FLYOUT_ALPHA_ANIMATION_DURATION = 100;
- /** Max width of the flyout, in terms of percent of the screen width. */
- private static final float FLYOUT_MAX_WIDTH_PERCENT = .6f;
-
/** Percent to darken the bubbles when they're in the dismiss target. */
private static final float DARKEN_PERCENT = 0.3f;
@@ -152,17 +156,9 @@ public class BubbleStackView extends FrameLayout {
private FrameLayout mExpandedViewContainer;
- private FrameLayout mFlyoutContainer;
- private FrameLayout mFlyout;
- private TextView mFlyoutText;
- private ShapeDrawable mLeftFlyoutTriangle;
- private ShapeDrawable mRightFlyoutTriangle;
- /** Spring animation for the flyout. */
- private SpringAnimation mFlyoutSpring;
+ private BubbleFlyoutView mFlyout;
/** Runnable that fades out the flyout and then sets it to GONE. */
- private Runnable mHideFlyout =
- () -> mFlyoutContainer.animate().alpha(0f).withEndAction(
- () -> mFlyoutContainer.setVisibility(GONE));
+ private Runnable mHideFlyout = () -> animateFlyoutCollapsed(true, 0 /* velX */);
/** Layout change listener that moves the stack to the nearest valid position on rotation. */
private OnLayoutChangeListener mMoveStackToValidPositionOnLayoutListener;
@@ -176,9 +172,6 @@ public class BubbleStackView extends FrameLayout {
private int mBubbleSize;
private int mBubblePadding;
- private int mFlyoutPadding;
- private int mFlyoutSpaceFromBubble;
- private int mPointerSize;
private int mExpandedAnimateXDistance;
private int mExpandedAnimateYDistance;
private int mStatusBarHeight;
@@ -189,8 +182,11 @@ public class BubbleStackView extends FrameLayout {
private boolean mIsExpanded;
private boolean mImeVisible;
- /** Whether the stack is currently being dragged. */
- private boolean mIsDragging = false;
+ /** Whether the stack is currently on the left side of the screen, or animating there. */
+ private boolean mStackOnLeftOrWillBe = false;
+
+ /** Whether a touch gesture, such as a stack/bubble drag or flyout drag, is in progress. */
+ private boolean mIsGestureInProgress = false;
private BubbleTouchHandler mTouchHandler;
private BubbleController.BubbleExpandListener mExpandListener;
@@ -249,6 +245,40 @@ public class BubbleStackView extends FrameLayout {
}
};
+ /** Float property that 'drags' the flyout. */
+ private final FloatPropertyCompat mFlyoutCollapseProperty =
+ new FloatPropertyCompat("FlyoutCollapseSpring") {
+ @Override
+ public float getValue(Object o) {
+ return mFlyoutDragDeltaX;
+ }
+
+ @Override
+ public void setValue(Object o, float v) {
+ onFlyoutDragged(v);
+ }
+ };
+
+ /** SpringAnimation that springs the flyout collapsed via onFlyoutDragged. */
+ private final SpringAnimation mFlyoutTransitionSpring =
+ new SpringAnimation(this, mFlyoutCollapseProperty);
+
+ /** Distance the flyout has been dragged in the X axis. */
+ private float mFlyoutDragDeltaX = 0f;
+
+ /**
+ * End listener for the flyout spring that either posts a runnable to hide the flyout, or hides
+ * it immediately.
+ */
+ private final DynamicAnimation.OnAnimationEndListener mAfterFlyoutTransitionSpring =
+ (dynamicAnimation, b, v, v1) -> {
+ if (mFlyoutDragDeltaX == 0) {
+ mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
+ } else {
+ mFlyout.hideFlyout();
+ }
+ };
+
@NonNull private final SurfaceSynchronizer mSurfaceSynchronizer;
private BubbleDismissView mDismissContainer;
@@ -267,9 +297,6 @@ public class BubbleStackView extends FrameLayout {
Resources res = getResources();
mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
mBubblePadding = res.getDimensionPixelSize(R.dimen.bubble_padding);
- mFlyoutPadding = res.getDimensionPixelSize(R.dimen.bubble_flyout_padding_x);
- mFlyoutSpaceFromBubble = res.getDimensionPixelSize(R.dimen.bubble_flyout_space_from_bubble);
- mPointerSize = res.getDimensionPixelSize(R.dimen.bubble_flyout_pointer_size);
mExpandedAnimateXDistance =
res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_x_distance);
mExpandedAnimateYDistance =
@@ -307,17 +334,24 @@ public class BubbleStackView extends FrameLayout {
mExpandedViewContainer.setClipChildren(false);
addView(mExpandedViewContainer);
- mFlyoutContainer = (FrameLayout) mInflater.inflate(R.layout.bubble_flyout, this, false);
- mFlyoutContainer.setVisibility(GONE);
- mFlyoutContainer.setClipToPadding(false);
- mFlyoutContainer.setClipChildren(false);
- mFlyoutContainer.animate()
+ mFlyout = new BubbleFlyoutView(context);
+ mFlyout.setVisibility(GONE);
+ mFlyout.animate()
.setDuration(FLYOUT_ALPHA_ANIMATION_DURATION)
.setInterpolator(new AccelerateDecelerateInterpolator());
+ addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+
+ mFlyoutTransitionSpring.setSpring(new SpringForce()
+ .setStiffness(SpringForce.STIFFNESS_MEDIUM)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
+ mFlyoutTransitionSpring.addEndListener(mAfterFlyoutTransitionSpring);
- mFlyout = mFlyoutContainer.findViewById(R.id.bubble_flyout);
- addView(mFlyoutContainer);
- setupFlyout();
+ mDismissContainer = new BubbleDismissView(mContext);
+ mDismissContainer.setLayoutParams(new FrameLayout.LayoutParams(
+ MATCH_PARENT,
+ getResources().getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height),
+ Gravity.BOTTOM));
+ addView(mDismissContainer);
mDismissContainer = new BubbleDismissView(mContext);
mDismissContainer.setLayoutParams(new FrameLayout.LayoutParams(
@@ -742,7 +776,7 @@ public class BubbleStackView extends FrameLayout {
}
// Outside parts of view we care about.
return null;
- } else if (mFlyoutContainer.getVisibility() == VISIBLE && isIntersecting(mFlyout, x, y)) {
+ } else if (mFlyout.getVisibility() == VISIBLE && isIntersecting(mFlyout, x, y)) {
return mFlyout;
}
@@ -931,7 +965,6 @@ public class BubbleStackView extends FrameLayout {
mBubbleContainer.setController(mStackAnimationController);
hideFlyoutImmediate();
- mIsDragging = true;
mDraggingInDismissTarget = false;
}
@@ -948,20 +981,87 @@ public class BubbleStackView extends FrameLayout {
if (DEBUG) {
Log.d(TAG, "onDragFinish");
}
- // TODO: Add fling to bottom to dismiss.
- mIsDragging = false;
if (mIsExpanded || mIsExpansionAnimating) {
return;
}
- mStackAnimationController.flingStackThenSpringToEdge(x, velX, velY);
+ final float newStackX = mStackAnimationController.flingStackThenSpringToEdge(x, velX, velY);
logBubbleEvent(null /* no bubble associated with bubble stack move */,
StatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
+ mStackOnLeftOrWillBe = newStackX <= 0;
+ updateBubbleShadowsAndDotPosition(true /* animate */);
springOutDismissTargetAndHideCircle();
}
+ void onFlyoutDragStart() {
+ mFlyout.removeCallbacks(mHideFlyout);
+ }
+
+ void onFlyoutDragged(float deltaX) {
+ final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
+ mFlyoutDragDeltaX = deltaX;
+
+ final float collapsePercent =
+ onLeft ? -deltaX / mFlyout.getWidth() : deltaX / mFlyout.getWidth();
+ mFlyout.setCollapsePercent(Math.min(1f, Math.max(0f, collapsePercent)));
+
+ // Calculate how to translate the flyout if it has been dragged too far in etiher direction.
+ float overscrollTranslation = 0f;
+ if (collapsePercent < 0f || collapsePercent > 1f) {
+ // Whether we are more than 100% transitioned to the dot.
+ final boolean overscrollingPastDot = collapsePercent > 1f;
+
+ // Whether we are overscrolling physically to the left - this can either be pulling the
+ // flyout away from the stack (if the stack is on the right) or pushing it to the left
+ // after it has already become the dot.
+ final boolean overscrollingLeft =
+ (onLeft && collapsePercent > 1f) || (!onLeft && collapsePercent < 0f);
+
+ overscrollTranslation =
+ (overscrollingPastDot ? collapsePercent - 1f : collapsePercent * -1)
+ * (overscrollingLeft ? -1 : 1)
+ * (mFlyout.getWidth() / (FLYOUT_OVERSCROLL_ATTENUATION_FACTOR
+ // Attenuate the smaller dot less than the larger flyout.
+ / (overscrollingPastDot ? 2 : 1)));
+ }
+
+ mFlyout.setTranslationX(mFlyout.getRestingTranslationX() + overscrollTranslation);
+ }
+
+ /**
+ * Called when the flyout drag has finished, and returns true if the gesture successfully
+ * dismissed the flyout.
+ */
+ void onFlyoutDragFinished(float deltaX, float velX) {
+ final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
+ final boolean metRequiredVelocity =
+ onLeft ? velX < -FLYOUT_DISMISS_VELOCITY : velX > FLYOUT_DISMISS_VELOCITY;
+ final boolean metRequiredDeltaX =
+ onLeft
+ ? deltaX < -mFlyout.getWidth() * FLYOUT_DRAG_PERCENT_DISMISS
+ : deltaX > mFlyout.getWidth() * FLYOUT_DRAG_PERCENT_DISMISS;
+ final boolean isCancelFling = onLeft ? velX > 0 : velX < 0;
+ final boolean shouldDismiss = metRequiredVelocity || (metRequiredDeltaX && !isCancelFling);
+
+ mFlyout.removeCallbacks(mHideFlyout);
+ animateFlyoutCollapsed(shouldDismiss, velX);
+ }
+
+ /**
+ * Called when the first touch event of a gesture (stack drag, bubble drag, flyout drag, etc.)
+ * is received.
+ */
+ void onGestureStart() {
+ mIsGestureInProgress = true;
+ }
+
+ /** Called when a gesture is completed or cancelled. */
+ void onGestureFinished() {
+ mIsGestureInProgress = false;
+ }
+
/** Prepares and starts the desaturate/darken animation on the bubble stack. */
private void animateDesaturateAndDarken(View targetView, boolean desaturateAndDarken) {
mDesaturateAndDarkenTargetView = targetView;
@@ -1119,12 +1219,22 @@ public class BubbleStackView extends FrameLayout {
mShowingDismiss = false;
}
-
/** Whether the location of the given MotionEvent is within the dismiss target area. */
- public boolean isInDismissTarget(MotionEvent ev) {
+ boolean isInDismissTarget(MotionEvent ev) {
return isIntersecting(mDismissContainer.getDismissTarget(), ev.getRawX(), ev.getRawY());
}
+ /** Animates the flyout collapsed (to dot), or the reverse, starting with the given velocity. */
+ private void animateFlyoutCollapsed(boolean collapsed, float velX) {
+ final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
+ mFlyoutTransitionSpring
+ .setStartValue(mFlyoutDragDeltaX)
+ .setStartVelocity(velX)
+ .animateToFinalPosition(collapsed
+ ? (onLeft ? -mFlyout.getWidth() : mFlyout.getWidth())
+ : 0f);
+ }
+
/**
* Calculates how large the expanded view of the bubble can be. This takes into account the
* y position when the bubbles are expanded as well as the bounds of the dismiss target.
@@ -1161,55 +1271,27 @@ public class BubbleStackView extends FrameLayout {
final CharSequence updateMessage = bubble.entry.getUpdateMessage(getContext());
// Show the message if one exists, and we're not expanded or animating expansion.
- if (updateMessage != null && !isExpanded() && !mIsExpansionAnimating && !mIsDragging) {
- final PointF stackPos = mStackAnimationController.getStackPosition();
-
- // Set the flyout TextView's max width in terms of percent, and then subtract out the
- // padding so that the entire flyout view will be the desired width (rather than the
- // TextView being the desired width + extra padding).
- mFlyoutText.setMaxWidth(
- (int) (getWidth() * FLYOUT_MAX_WIDTH_PERCENT) - mFlyoutPadding * 2);
-
- mFlyoutContainer.setAlpha(0f);
- mFlyoutContainer.setVisibility(VISIBLE);
-
- mFlyoutText.setText(updateMessage);
-
- final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
-
- if (onLeft) {
- mLeftFlyoutTriangle.setAlpha(255);
- mRightFlyoutTriangle.setAlpha(0);
- } else {
- mLeftFlyoutTriangle.setAlpha(0);
- mRightFlyoutTriangle.setAlpha(255);
+ if (updateMessage != null
+ && !isExpanded()
+ && !mIsExpansionAnimating
+ && !mIsGestureInProgress) {
+ if (bubble.iconView != null) {
+ bubble.iconView.setSuppressDot(true /* suppressDot */, false /* animate */);
+ mFlyoutDragDeltaX = 0f;
+ mFlyout.setAlpha(0f);
+
+ // Post in case layout isn't complete and getWidth returns 0.
+ post(() -> mFlyout.showFlyout(
+ updateMessage, mStackAnimationController.getStackPosition(), getWidth(),
+ mStackAnimationController.isStackOnLeftSide(),
+ bubble.iconView.getBadgeColor(),
+ () -> {
+ bubble.iconView.setSuppressDot(
+ false /* suppressDot */, false /* animate */);
+ }));
}
-
- mFlyoutContainer.post(() -> {
- // Multi line flyouts get top-aligned to the bubble.
- if (mFlyoutText.getLineCount() > 1) {
- mFlyoutContainer.setTranslationY(stackPos.y);
- } else {
- // Single line flyouts are vertically centered with respect to the bubble.
- mFlyoutContainer.setTranslationY(
- stackPos.y + (mBubbleSize - mFlyout.getHeight()) / 2f);
- }
-
- final float destinationX = onLeft
- ? stackPos.x + mBubbleSize + mFlyoutSpaceFromBubble
- : stackPos.x - mFlyoutContainer.getWidth() - mFlyoutSpaceFromBubble;
-
- // Translate towards the stack slightly, then spring out from the stack.
- mFlyoutContainer.setTranslationX(
- destinationX + (onLeft ? -mBubblePadding : mBubblePadding));
-
- mFlyoutContainer.animate().alpha(1f);
- mFlyoutSpring.animateToFinalPosition(destinationX);
-
- mFlyout.removeCallbacks(mHideFlyout);
- mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
- });
-
+ mFlyout.removeCallbacks(mHideFlyout);
+ mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__FLYOUT);
}
}
@@ -1217,7 +1299,7 @@ public class BubbleStackView extends FrameLayout {
/** Hide the flyout immediately and cancel any pending hide runnables. */
private void hideFlyoutImmediate() {
mFlyout.removeCallbacks(mHideFlyout);
- mHideFlyout.run();
+ mFlyout.hideFlyout();
}
@Override
@@ -1230,7 +1312,7 @@ public class BubbleStackView extends FrameLayout {
mBubbleContainer.getBoundsOnScreen(outRect);
}
- if (mFlyoutContainer.getVisibility() == View.VISIBLE) {
+ if (mFlyout.getVisibility() == View.VISIBLE) {
final Rect flyoutBounds = new Rect();
mFlyout.getBoundsOnScreen(flyoutBounds);
outRect.union(flyoutBounds);
@@ -1287,78 +1369,11 @@ public class BubbleStackView extends FrameLayout {
}
}
- /** Sets up the flyout views and drawables. */
- private void setupFlyout() {
- // Retrieve the styled floating background color.
- TypedArray ta = mContext.obtainStyledAttributes(
- new int[]{android.R.attr.colorBackgroundFloating});
- final int floatingBackgroundColor = ta.getColor(0, Color.WHITE);
- ta.recycle();
-
- // Retrieve the flyout background, which is currently a rounded white rectangle with a
- // shadow but no triangular arrow pointing anywhere.
- final LayerDrawable flyoutBackground = (LayerDrawable) mFlyout.getBackground();
-
- // Create the triangle drawables and set their color.
- mLeftFlyoutTriangle =
- new ShapeDrawable(TriangleShape.createHorizontal(
- mPointerSize, mPointerSize, true /* isPointingLeft */));
- mRightFlyoutTriangle =
- new ShapeDrawable(TriangleShape.createHorizontal(
- mPointerSize, mPointerSize, false /* isPointingLeft */));
- mLeftFlyoutTriangle.getPaint().setColor(floatingBackgroundColor);
- mRightFlyoutTriangle.getPaint().setColor(floatingBackgroundColor);
-
- // Add both triangles to the drawable. We'll show and hide the appropriate ones when we show
- // the flyout.
- final int leftTriangleIndex = flyoutBackground.addLayer(mLeftFlyoutTriangle);
- flyoutBackground.setLayerSize(leftTriangleIndex, mPointerSize, mPointerSize);
- flyoutBackground.setLayerGravity(leftTriangleIndex, Gravity.LEFT | Gravity.CENTER_VERTICAL);
- flyoutBackground.setLayerInsetLeft(leftTriangleIndex, -mPointerSize);
-
- final int rightTriangleIndex = flyoutBackground.addLayer(mRightFlyoutTriangle);
- flyoutBackground.setLayerSize(rightTriangleIndex, mPointerSize, mPointerSize);
- flyoutBackground.setLayerGravity(
- rightTriangleIndex, Gravity.RIGHT | Gravity.CENTER_VERTICAL);
- flyoutBackground.setLayerInsetRight(rightTriangleIndex, -mPointerSize);
-
- // Append the appropriate triangle's outline to the view's outline so that the shadows look
- // correct.
- mFlyout.setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- final boolean leftPointing = mStackAnimationController.isStackOnLeftSide();
-
- // Get the outline from the appropriate triangle.
- final Outline triangleOutline = new Outline();
- if (leftPointing) {
- mLeftFlyoutTriangle.getOutline(triangleOutline);
- } else {
- mRightFlyoutTriangle.getOutline(triangleOutline);
- }
-
- // Offset it to the correct position, since it has no intrinsic position since
- // that is maintained by the parent LayerDrawable.
- triangleOutline.offset(
- leftPointing ? -mPointerSize : mFlyout.getWidth(),
- mFlyout.getHeight() / 2 - mPointerSize / 2);
-
- // Merge the outlines.
- final Outline compoundOutline = new Outline();
- flyoutBackground.getOutline(compoundOutline);
- compoundOutline.mPath.addPath(triangleOutline.mPath);
- outline.set(compoundOutline);
- }
- });
-
- mFlyoutText = mFlyout.findViewById(R.id.bubble_flyout_text);
- mFlyoutSpring = new SpringAnimation(mFlyoutContainer, DynamicAnimation.TRANSLATION_X);
- }
-
private void applyCurrentState() {
if (DEBUG) {
Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded);
}
+
mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
if (mIsExpanded) {
// First update the view so that it calculates a new height (ensuring the y position
@@ -1376,10 +1391,15 @@ public class BubbleStackView extends FrameLayout {
}
}
+ mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
+ updateBubbleShadowsAndDotPosition(false);
+ }
+
+ /** Sets the appropriate Z-order and dot position for each bubble in the stack. */
+ private void updateBubbleShadowsAndDotPosition(boolean animate) {
int bubbsCount = mBubbleContainer.getChildCount();
for (int i = 0; i < bubbsCount; i++) {
BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
- bv.updateDotVisibility();
bv.setZ((BubbleController.MAX_BUBBLES
* getResources().getDimensionPixelSize(R.dimen.bubble_elevation)) - i);
@@ -1393,6 +1413,11 @@ public class BubbleStackView extends FrameLayout {
}
});
bv.setClipToOutline(false);
+
+ // If the dot is on the left, and so is the stack, we need to change the dot position.
+ if (bv.getDotPositionOnLeft() == mStackOnLeftOrWillBe) {
+ bv.setDotPosition(!mStackOnLeftOrWillBe, animate);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index f429c2c124b3..8fe8bd305707 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -111,12 +111,13 @@ class BubbleTouchHandler implements View.OnTouchListener {
trackMovement(event);
mTouchDown.set(rawX, rawY);
+ mStack.onGestureStart();
if (isStack) {
mViewPositionOnTouchDown.set(mStack.getStackPosition());
mStack.onDragStart();
} else if (isFlyout) {
- // TODO(b/129768381): Make the flyout dismissable with a gesture.
+ mStack.onFlyoutDragStart();
} else {
mViewPositionOnTouchDown.set(
mTouchedView.getTranslationX(), mTouchedView.getTranslationY());
@@ -137,7 +138,7 @@ class BubbleTouchHandler implements View.OnTouchListener {
if (isStack) {
mStack.onDragged(viewX, viewY);
} else if (isFlyout) {
- // TODO(b/129768381): Make the flyout dismissable with a gesture.
+ mStack.onFlyoutDragged(deltaX);
} else {
mStack.onBubbleDragged(mTouchedView, viewX, viewY);
}
@@ -152,8 +153,10 @@ class BubbleTouchHandler implements View.OnTouchListener {
final float velY = mVelocityTracker.getYVelocity();
// If the touch event is within the dismiss target, magnet the stack to it.
- mStack.animateMagnetToDismissTarget(
- mTouchedView, mInDismissTarget, viewX, viewY, velX, velY);
+ if (!isFlyout) {
+ mStack.animateMagnetToDismissTarget(
+ mTouchedView, mInDismissTarget, viewX, viewY, velX, velY);
+ }
}
break;
@@ -174,7 +177,9 @@ class BubbleTouchHandler implements View.OnTouchListener {
: mInDismissTarget
|| velY > INDIVIDUAL_BUBBLE_DISMISS_MIN_VELOCITY;
- if (shouldDismiss) {
+ if (isFlyout && mMovedEnough) {
+ mStack.onFlyoutDragFinished(rawX - mTouchDown.x /* deltaX */, velX);
+ } else if (shouldDismiss) {
final String individualBubbleKey =
isStack ? null : ((BubbleView) mTouchedView).getKey();
mStack.magnetToStackIfNeededThenAnimateDismissal(mTouchedView, velX, velY,
@@ -200,7 +205,7 @@ class BubbleTouchHandler implements View.OnTouchListener {
}
} else if (mTouchedView == mStack.getExpandedBubbleView()) {
mBubbleData.setExpanded(false);
- } else if (isStack) {
+ } else if (isStack || isFlyout) {
// Toggle expansion
mBubbleData.setExpanded(!mBubbleData.isExpanded());
} else {
@@ -251,9 +256,12 @@ class BubbleTouchHandler implements View.OnTouchListener {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
+
mTouchedView = null;
mMovedEnough = false;
mInDismissTarget = false;
+
+ mStack.onGestureFinished();
}
private void trackMovement(MotionEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 2681b6d0c891..aa32b9456cbc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -48,9 +48,12 @@ public class BubbleView extends FrameLayout {
private Context mContext;
private BadgedImageView mBadgedImageView;
+ private int mBadgeColor;
private int mPadding;
private int mIconInset;
+ private boolean mSuppressDot = false;
+
private NotificationEntry mEntry;
public BubbleView(Context context) {
@@ -130,18 +133,54 @@ public class BubbleView extends FrameLayout {
return (mEntry != null) ? mEntry.getRow() : null;
}
+ /** Changes the dot's visibility to match the bubble view's state. */
+ void updateDotVisibility(boolean animate) {
+ updateDotVisibility(animate, null /* after */);
+ }
+
+ /**
+ * Changes the dot's visibility to match the bubble view's state, running the provided callback
+ * after animation if requested.
+ */
+ void updateDotVisibility(boolean animate, Runnable after) {
+ boolean showDot = getEntry().showInShadeWhenBubble() && !mSuppressDot;
+
+ if (animate) {
+ animateDot(showDot, after);
+ } else {
+ mBadgedImageView.setShowDot(showDot);
+ }
+ }
+
/**
- * Marks this bubble as "read", i.e. no badge should show.
+ * Sets whether or not to hide the dot even if we'd otherwise show it. This is used while the
+ * flyout is visible or animating, to hide the dot until the flyout visually transforms into it.
*/
- public void updateDotVisibility() {
- boolean showDot = getEntry().showInShadeWhenBubble();
- animateDot(showDot);
+ void setSuppressDot(boolean suppressDot, boolean animate) {
+ mSuppressDot = suppressDot;
+ updateDotVisibility(animate);
+ }
+
+ /** Sets the position of the 'new' dot, animating it out and back in if requested. */
+ void setDotPosition(boolean onLeft, boolean animate) {
+ if (animate && onLeft != mBadgedImageView.getDotPosition() && !mSuppressDot) {
+ animateDot(false /* showDot */, () -> {
+ mBadgedImageView.setDotPosition(onLeft);
+ animateDot(true /* showDot */, null);
+ });
+ } else {
+ mBadgedImageView.setDotPosition(onLeft);
+ }
+ }
+
+ boolean getDotPositionOnLeft() {
+ return mBadgedImageView.getDotPosition();
}
/**
* Animates the badge to show or hide.
*/
- private void animateDot(boolean showDot) {
+ private void animateDot(boolean showDot, Runnable after) {
if (mBadgedImageView.isShowingDot() != showDot) {
mBadgedImageView.setShowDot(showDot);
mBadgedImageView.clearAnimation();
@@ -152,9 +191,13 @@ public class BubbleView extends FrameLayout {
fraction = showDot ? fraction : 1 - fraction;
mBadgedImageView.setDotScale(fraction);
}).withEndAction(() -> {
- if (!showDot) {
- mBadgedImageView.setShowDot(false);
- }
+ if (!showDot) {
+ mBadgedImageView.setShowDot(false);
+ }
+
+ if (after != null) {
+ after.run();
+ }
}).start();
}
}
@@ -181,8 +224,13 @@ public class BubbleView extends FrameLayout {
mBadgedImageView.setImageDrawable(iconDrawable);
}
int badgeColor = determineDominateColor(iconDrawable, n.color);
+ mBadgeColor = badgeColor;
mBadgedImageView.setDotColor(badgeColor);
- animateDot(mEntry.showInShadeWhenBubble() /* showDot */);
+ animateDot(mEntry.showInShadeWhenBubble() /* showDot */, null /* after */);
+ }
+
+ int getBadgeColor() {
+ return mBadgeColor;
}
private Drawable buildIconWithTint(Drawable iconDrawable, int backgroundColor) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index f937525cf417..8529ed42cf0a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -225,8 +225,10 @@ public class StackAnimationController extends
/**
* Flings the stack starting with the given velocities, springing it to the nearest edge
* afterward.
+ *
+ * @return The X value that the stack will end up at after the fling/spring.
*/
- public void flingStackThenSpringToEdge(float x, float velX, float velY) {
+ public float flingStackThenSpringToEdge(float x, float velX, float velY) {
final boolean stackOnLeftSide = x - mIndividualBubbleSize / 2 < mLayout.getWidth() / 2;
final boolean stackShouldFlingLeft = stackOnLeftSide
@@ -281,6 +283,7 @@ public class StackAnimationController extends
DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
mIsMovingFromFlinging = true;
+ return destinationRelativeX;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index de1069064518..05665b5ae4a2 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -16,8 +16,6 @@
package com.android.systemui.colorextraction;
-import android.annotation.ColorInt;
-import android.annotation.IntDef;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.content.Context;
@@ -36,13 +34,10 @@ import com.android.internal.colorextraction.types.ExtractionType;
import com.android.internal.colorextraction.types.Tonal;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
-import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.policy.ConfigurationController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import javax.inject.Inject;
@@ -55,41 +50,23 @@ import javax.inject.Singleton;
public class SysuiColorExtractor extends ColorExtractor implements Dumpable,
ConfigurationController.ConfigurationListener {
private static final String TAG = "SysuiColorExtractor";
-
- public static final int SCRIM_TYPE_REGULAR = 1;
- public static final int SCRIM_TYPE_LIGHT = 2;
- public static final int SCRIM_TYPE_DARK = 3;
-
- @IntDef(prefix = {"SCRIM_TYPE_"}, value = {
- SCRIM_TYPE_REGULAR,
- SCRIM_TYPE_LIGHT,
- SCRIM_TYPE_DARK
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ScrimType {
- }
-
private final Tonal mTonal;
- private final OverviewProxyService mOverviewProxyService;
private boolean mWallpaperVisible;
private boolean mHasBackdrop;
// Colors to return when the wallpaper isn't visible
private final GradientColors mWpHiddenColors;
@Inject
- public SysuiColorExtractor(Context context, ConfigurationController configurationController,
- OverviewProxyService overviewProxyService) {
- this(context, new Tonal(context), configurationController, true, overviewProxyService);
+ public SysuiColorExtractor(Context context, ConfigurationController configurationController) {
+ this(context, new Tonal(context), configurationController, true);
}
@VisibleForTesting
public SysuiColorExtractor(Context context, ExtractionType type,
- ConfigurationController configurationController, boolean registerVisibility,
- OverviewProxyService overviewProxyService) {
+ ConfigurationController configurationController, boolean registerVisibility) {
super(context, type, false /* immediately */);
mTonal = type instanceof Tonal ? (Tonal) type : new Tonal(context);
mWpHiddenColors = new GradientColors();
- mOverviewProxyService = overviewProxyService;
configurationController.addCallback(this);
WallpaperColors systemColors = getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
@@ -133,35 +110,17 @@ public class SysuiColorExtractor extends ColorExtractor implements Dumpable,
return;
}
+ super.onColorsChanged(colors, which);
+
if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
updateDefaultGradients(colors);
}
- super.onColorsChanged(colors, which);
}
@Override
public void onUiModeChanged() {
WallpaperColors systemColors = getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
updateDefaultGradients(systemColors);
- triggerColorsChanged(WallpaperManager.FLAG_SYSTEM);
- }
-
- @Override
- protected void triggerColorsChanged(int which) {
- super.triggerColorsChanged(which);
-
- if (mWpHiddenColors != null && (which & WallpaperManager.FLAG_SYSTEM) != 0) {
- @ColorInt int colorInt = mWpHiddenColors.getMainColor();
- @ScrimType int scrimType;
- if (colorInt == Tonal.MAIN_COLOR_LIGHT) {
- scrimType = SCRIM_TYPE_LIGHT;
- } else if (colorInt == Tonal.MAIN_COLOR_DARK) {
- scrimType = SCRIM_TYPE_DARK;
- } else {
- scrimType = SCRIM_TYPE_REGULAR;
- }
- mOverviewProxyService.onScrimColorsChanged(colorInt, scrimType);
- }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index e87ff520e028..dcabb780ee3a 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -25,6 +25,7 @@ import android.app.ActivityManager;
import android.app.Dialog;
import android.app.KeyguardManager;
import android.app.PendingIntent;
+import android.app.StatusBarManager;
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
@@ -38,7 +39,9 @@ import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.ConnectivityManager;
+import android.os.Binder;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -75,6 +78,7 @@ import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.colorextraction.drawable.ScrimDrawable;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.util.EmergencyAffordanceManager;
@@ -1501,6 +1505,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final Context mContext;
private final MyAdapter mAdapter;
+ private final IStatusBarService mStatusBarService;
+ private final IBinder mToken = new Binder();
private MultiListLayout mGlobalActionsLayout;
private Drawable mBackgroundDrawable;
private final SysuiColorExtractor mColorExtractor;
@@ -1516,6 +1522,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mContext = context;
mAdapter = adapter;
mColorExtractor = Dependency.get(SysuiColorExtractor.class);
+ mStatusBarService = Dependency.get(IStatusBarService.class);
// Window initialization
Window window = getWindow();
@@ -1542,9 +1549,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
private boolean shouldUsePanel() {
- return isPanelEnabled(mContext)
- && mPanelController != null
- && mPanelController.getPanelContent() != null;
+ return mPanelController != null && mPanelController.getPanelContent() != null;
}
private void initializePanel() {
@@ -1574,6 +1579,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mContext, true, RotationUtils.ROTATION_NONE);
}
+ // Disable rotation suggestions, if enabled
+ setRotationSuggestionsEnabled(false);
+
FrameLayout panelContainer = new FrameLayout(mContext);
FrameLayout.LayoutParams panelParams =
new FrameLayout.LayoutParams(
@@ -1732,11 +1740,24 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
}
+ private void setRotationSuggestionsEnabled(boolean enabled) {
+ try {
+ final int userId = Binder.getCallingUserHandle().getIdentifier();
+ final int what = enabled
+ ? StatusBarManager.DISABLE2_NONE
+ : StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS;
+ mStatusBarService.disable2ForUser(what, mToken, mContext.getPackageName(), userId);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
private void resetOrientation() {
if (mResetOrientationData != null) {
RotationPolicy.setRotationLockAtAngle(mContext, mResetOrientationData.locked,
mResetOrientationData.rotation);
}
+ setRotationSuggestionsEnabled(true);
}
@Override
@@ -1792,15 +1813,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
/**
- * Determines whether or not the Global Actions Panel should appear when the power button
- * is held.
- */
- private static boolean isPanelEnabled(Context context) {
- return FeatureFlagUtils.isEnabled(
- context, FeatureFlagUtils.GLOBAL_ACTIONS_PANEL_ENABLED);
- }
-
- /**
* Determines whether the Global Actions menu should use a separated view for emergency actions.
*/
private static boolean shouldUseSeparatedView() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index c5591cf9d947..78c7cd406ba1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -31,7 +31,6 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_B
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
-import android.annotation.ColorInt;
import android.annotation.FloatRange;
import android.app.ActivityTaskManager;
import android.content.BroadcastReceiver;
@@ -60,7 +59,6 @@ import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.colorextraction.SysuiColorExtractor.ScrimType;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -537,16 +535,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
dispatchNavButtonBounds();
}
- public void onScrimColorsChanged(@ColorInt int color, @ScrimType int type) {
- if (mOverviewProxy != null) {
- try {
- mOverviewProxy.onScrimColorsChanged(color, type);
- } catch (RemoteException e) {
- Log.e(TAG_OPS, "Failed to call onScrimColorsChanged()", e);
- }
- }
- }
-
private void dispatchNavButtonBounds() {
if (mOverviewProxy != null && mActiveNavBarRegion != null) {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index a76c9dc9f40a..fd76a79eab2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -189,6 +189,7 @@ public class KeyguardIndicationController implements StateListener,
mLockscreenGestureLogger.write(MetricsProto.MetricsEvent.ACTION_LS_LOCK,
0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
showTransientIndication(R.string.keyguard_indication_trust_disabled);
+ mKeyguardUpdateMonitor.onLockIconPressed();
mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser());
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 2cca701ef582..d202190724f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -71,6 +71,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
private int mIconAppearTopPadding;
private int mShelfAppearTranslation;
private float mDarkShelfPadding;
+ private float mDarkShelfIconSize;
private int mStatusBarHeight;
private int mStatusBarPaddingStart;
private AmbientState mAmbientState;
@@ -151,6 +152,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
mScrollFastThreshold = res.getDimensionPixelOffset(R.dimen.scroll_fast_threshold);
mShowNotificationShelf = res.getBoolean(R.bool.config_showNotificationShelf);
mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
+ mDarkShelfIconSize = res.getDimensionPixelOffset(R.dimen.dark_shelf_icon_size);
mGapHeight = res.getDimensionPixelSize(R.dimen.qs_notification_padding);
if (!mShowNotificationShelf) {
@@ -705,12 +707,13 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
notificationIconPosition += iconTopPadding;
float shelfIconPosition = getTranslationY() + icon.getTop();
- shelfIconPosition += (icon.getHeight() - icon.getIconScale() * mIconSize) / 2.0f;
+ float iconSize = mDark ? mDarkShelfIconSize : mIconSize;
+ shelfIconPosition += (icon.getHeight() - icon.getIconScale() * iconSize) / 2.0f;
float iconYTranslation = NotificationUtils.interpolate(
notificationIconPosition - shelfIconPosition,
0,
transitionAmount);
- float shelfIconSize = mIconSize * icon.getIconScale();
+ float shelfIconSize = iconSize * icon.getIconScale();
float alpha = 1.0f;
boolean noIcon = !row.isShowingIcon();
if (noIcon) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 1074f3af6b1d..f93c5f0827ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -29,6 +29,7 @@ import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.biometrics.BiometricSourceType;
import android.os.Handler;
+import android.os.Trace;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -89,6 +90,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
private float mDozeAmount;
private int mIconRes;
private boolean mWasPulsingOnThisFrame;
+ private boolean mWakeAndUnlockRunning;
private final Runnable mDrawOffTimeout = () -> update(true /* forceUpdate */);
private final DockManager.DockEventListener mDockEventListener =
@@ -255,9 +257,12 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
if (getDrawable() == animation && state == getState()
&& doesAnimationLoop(iconAnimRes)) {
animation.start();
+ } else {
+ Trace.endAsyncSection("LockIcon#Animation", state);
}
}
});
+ Trace.beginAsyncSection("LockIcon#Animation", state);
animation.start();
}
}
@@ -277,7 +282,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
mLastBouncerVisible = mBouncerVisible;
}
- boolean invisible = mDozing && (!mPulsing || mDocked);
+ boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked);
+ boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning;
setVisibility(invisible ? INVISIBLE : VISIBLE);
updateClickability();
}
@@ -450,4 +456,23 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
public void onUnlockMethodStateChanged() {
update();
}
+
+ /**
+ * We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the
+ * icon on top of the black front scrim.
+ */
+ public void onBiometricAuthModeChanged(boolean wakeAndUnlock) {
+ if (wakeAndUnlock) {
+ mWakeAndUnlockRunning = true;
+ }
+ update();
+ }
+
+ /**
+ * Triggered after the unlock animation is over and the user is looking at launcher.
+ */
+ public void onKeyguardFadedAway() {
+ mWakeAndUnlockRunning = false;
+ update();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index b4b4235909d9..17f0d5a79034 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -538,6 +538,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
if (mKeyguardMonitor.isKeyguardFadingAway()) {
mStatusBarKeyguardViewManager.onKeyguardFadedAway();
+ mStatusBarWindow.onKeyguardFadedAway();
}
}
@@ -3798,6 +3799,7 @@ public class StatusBar extends SystemUI implements DemoMode,
public void notifyBiometricAuthModeChanged() {
updateDozing();
updateScrimController();
+ mStatusBarWindow.onBiometricAuthModeChanged(mBiometricUnlockController.isWakeAndUnlock());
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 9f538bb3fca4..712e96255b02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -262,7 +262,28 @@ public class StatusBarWindowView extends FrameLayout {
* Propagate {@link StatusBar} pulsing state.
*/
public void setPulsing(boolean pulsing) {
- mLockIcon.setPulsing(pulsing);
+ if (mLockIcon != null) {
+ mLockIcon.setPulsing(pulsing);
+ }
+ }
+
+ /**
+ * Called when the biometric authentication mode changes.
+ * @param wakeAndUnlock If the type is {@link BiometricUnlockController#isWakeAndUnlock()}
+ */
+ public void onBiometricAuthModeChanged(boolean wakeAndUnlock) {
+ if (mLockIcon != null) {
+ mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock);
+ }
+ }
+
+ /**
+ * Called after finished unlocking and the status bar window is already collapsed.
+ */
+ public void onKeyguardFadedAway() {
+ if (mLockIcon != null) {
+ mLockIcon.onKeyguardFadedAway();
+ }
}
public void setStatusBarView(PhoneStatusBarView statusBarView) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleFlyoutViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleFlyoutViewTest.java
new file mode 100644
index 000000000000..173237f7b311
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleFlyoutViewTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotSame;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Color;
+import android.graphics.PointF;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class BubbleFlyoutViewTest extends SysuiTestCase {
+ private BubbleFlyoutView mFlyout;
+ private TextView mFlyoutText;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mFlyout = new BubbleFlyoutView(getContext());
+
+ mFlyoutText = mFlyout.findViewById(R.id.bubble_flyout_text);
+ }
+
+ @Test
+ public void testShowFlyout_isVisible() {
+ mFlyout.showFlyout("Hello", new PointF(100, 100), 500, true, Color.WHITE, null);
+ assertEquals("Hello", mFlyoutText.getText());
+ assertEquals(View.VISIBLE, mFlyout.getVisibility());
+ assertEquals(1f, mFlyoutText.getAlpha(), .01f);
+ }
+
+ @Test
+ public void testFlyoutHide_runsCallback() {
+ Runnable after = Mockito.mock(Runnable.class);
+ mFlyout.showFlyout("Hello", new PointF(100, 100), 500, true, Color.WHITE, after);
+ mFlyout.hideFlyout();
+
+ verify(after).run();
+ }
+
+ @Test
+ public void testSetCollapsePercent() {
+ mFlyout.showFlyout("Hello", new PointF(100, 100), 500, true, Color.WHITE, null);
+
+ float initialTranslationZ = mFlyout.getTranslationZ();
+
+ mFlyout.setCollapsePercent(1f);
+ assertEquals(0f, mFlyoutText.getAlpha(), 0.01f);
+ assertNotSame(0f, mFlyoutText.getTranslationX()); // Should have moved to collapse.
+ assertTrue(mFlyout.getTranslationZ() < initialTranslationZ); // Should be descending.
+
+ mFlyout.setCollapsePercent(0f);
+ assertEquals(1f, mFlyoutText.getAlpha(), 0.01f);
+ assertEquals(0f, mFlyoutText.getTranslationX());
+ assertEquals(initialTranslationZ, mFlyout.getTranslationZ());
+
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java
deleted file mode 100644
index bafae6ce737a..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bubbles;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.widget.TextView;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class BubbleStackViewTest extends SysuiTestCase {
- private BubbleStackView mStackView;
- @Mock private Bubble mBubble;
- @Mock private NotificationEntry mNotifEntry;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mStackView = new BubbleStackView(mContext, new BubbleData(getContext()), null);
- mBubble.entry = mNotifEntry;
- }
-
- @Test
- public void testAnimateInFlyoutForBubble() {
- when(mNotifEntry.getUpdateMessage(any())).thenReturn("Test Flyout Message.");
- mStackView.animateInFlyoutForBubble(mBubble);
-
- assertEquals("Test Flyout Message.",
- ((TextView) mStackView.findViewById(R.id.bubble_flyout_text)).getText());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
index 3d3c29564910..67df60a3dcfc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
@@ -34,14 +34,10 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.colorextraction.types.Tonal;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
/**
* Tests color extraction generation.
@@ -57,13 +53,6 @@ public class SysuiColorExtractorTests extends SysuiTestCase {
ColorExtractor.TYPE_NORMAL,
ColorExtractor.TYPE_DARK,
ColorExtractor.TYPE_EXTRA_DARK};
- @Mock
- private OverviewProxyService mOverviewProxyService;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- }
@Test
public void getColors_usesGreyIfWallpaperNotVisible() {
@@ -129,8 +118,7 @@ public class SysuiColorExtractorTests extends SysuiTestCase {
Tonal tonal = mock(Tonal.class);
ConfigurationController configurationController = mock(ConfigurationController.class);
SysuiColorExtractor sysuiColorExtractor = new SysuiColorExtractor(getContext(),
- tonal, configurationController, false /* registerVisibility */,
- mOverviewProxyService);
+ tonal, configurationController, false /* registerVisibility */);
verify(configurationController).addCallback(eq(sysuiColorExtractor));
reset(tonal);
@@ -145,7 +133,7 @@ public class SysuiColorExtractorTests extends SysuiTestCase {
outGradientColorsNormal.set(colors);
outGradientColorsDark.set(colors);
outGradientColorsExtraDark.set(colors);
- }, mock(ConfigurationController.class), false, mOverviewProxyService);
+ }, mock(ConfigurationController.class), false);
}
private void simulateEvent(SysuiColorExtractor extractor) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 6d9a77c58227..daee55bd3d61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -261,6 +261,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
longClickCaptor.getValue().onLongClick(mLockIcon);
verify(mLockPatternUtils).requireCredentialEntry(anyInt());
+ verify(mKeyguardUpdateMonitor).onLockIconPressed();
}
@Test
diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml
index 9694e76e138b..f1d2e0b27353 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml
@@ -37,6 +37,10 @@
{@link Window#setEnsuringNavigationBarContrastWhenTransparent}. -->
<bool name="config_navBarNeedsScrim">false</bool>
+ <!-- Controls whether seamless rotation should be allowed even though the navbar can move
+ (which normally prevents seamless rotation). -->
+ <bool name="config_allowSeamlessRotationDespiteNavBarMoving">true</bool>
+
<!-- Controls whether the side edge gestures can always trigger the transient nav bar to
show. -->
<bool name="config_navBarAlwaysShowOnSideEdgeGesture">true</bool>
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index facc2994507d..f12bfc33ea1b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3497,7 +3497,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
@Override
public void startCaptivePortalAppInternal(Network network, Bundle appExtras) {
- mContext.checkCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ mContext.enforceCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ "ConnectivityService");
final Intent appIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
appIntent.putExtras(appExtras);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index c6461491b8cd..fc355b7cce2f 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -286,6 +286,7 @@ class StorageManagerService extends IStorageManager.Stub
private static final String ATTR_NICKNAME = "nickname";
private static final String ATTR_USER_FLAGS = "userFlags";
private static final String ATTR_CREATED_MILLIS = "createdMillis";
+ private static final String ATTR_LAST_SEEN_MILLIS = "lastSeenMillis";
private static final String ATTR_LAST_TRIM_MILLIS = "lastTrimMillis";
private static final String ATTR_LAST_BENCH_MILLIS = "lastBenchMillis";
@@ -1313,7 +1314,7 @@ class StorageManagerService extends IStorageManager.Stub
private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
// Remember that we saw this volume so we're ready to accept user
// metadata, or so we can annoy them when a private volume is ejected
- if (vol.isMountedReadable() && !TextUtils.isEmpty(vol.fsUuid)) {
+ if (!TextUtils.isEmpty(vol.fsUuid)) {
VolumeRecord rec = mRecords.get(vol.fsUuid);
if (rec == null) {
rec = new VolumeRecord(vol.type, vol.fsUuid);
@@ -1323,14 +1324,15 @@ class StorageManagerService extends IStorageManager.Stub
rec.nickname = vol.disk.getDescription();
}
mRecords.put(rec.fsUuid, rec);
- writeSettingsLocked();
} else {
// Handle upgrade case where we didn't store partition GUID
if (TextUtils.isEmpty(rec.partGuid)) {
rec.partGuid = vol.partGuid;
- writeSettingsLocked();
}
}
+
+ rec.lastSeenMillis = System.currentTimeMillis();
+ writeSettingsLocked();
}
mCallbacks.notifyVolumeStateChanged(vol, oldState, newState);
@@ -1731,9 +1733,10 @@ class StorageManagerService extends IStorageManager.Stub
meta.partGuid = readStringAttribute(in, ATTR_PART_GUID);
meta.nickname = readStringAttribute(in, ATTR_NICKNAME);
meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS);
- meta.createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
- meta.lastTrimMillis = readLongAttribute(in, ATTR_LAST_TRIM_MILLIS);
- meta.lastBenchMillis = readLongAttribute(in, ATTR_LAST_BENCH_MILLIS);
+ meta.createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS, 0);
+ meta.lastSeenMillis = readLongAttribute(in, ATTR_LAST_SEEN_MILLIS, 0);
+ meta.lastTrimMillis = readLongAttribute(in, ATTR_LAST_TRIM_MILLIS, 0);
+ meta.lastBenchMillis = readLongAttribute(in, ATTR_LAST_BENCH_MILLIS, 0);
return meta;
}
@@ -1745,6 +1748,7 @@ class StorageManagerService extends IStorageManager.Stub
writeStringAttribute(out, ATTR_NICKNAME, rec.nickname);
writeIntAttribute(out, ATTR_USER_FLAGS, rec.userFlags);
writeLongAttribute(out, ATTR_CREATED_MILLIS, rec.createdMillis);
+ writeLongAttribute(out, ATTR_LAST_SEEN_MILLIS, rec.lastSeenMillis);
writeLongAttribute(out, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis);
writeLongAttribute(out, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis);
out.endTag(null, TAG_VOLUME);
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index f9aaf11c75f0..a7fb99f8b004 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -140,6 +140,7 @@ public class Watchdog extends Thread {
private boolean mCompleted;
private Monitor mCurrentMonitor;
private long mStartTime;
+ private int mPauseCount;
HandlerChecker(Handler handler, String name, long waitMaxMillis) {
mHandler = handler;
@@ -160,17 +161,18 @@ public class Watchdog extends Thread {
mMonitors.addAll(mMonitorQueue);
mMonitorQueue.clear();
}
- if (mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling()) {
+ if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling())
+ || (mPauseCount > 0)) {
+ // Don't schedule until after resume OR
// If the target looper has recently been polling, then
// there is no reason to enqueue our checker on it since that
// is as good as it not being deadlocked. This avoid having
- // to do a context switch to check the thread. Note that we
- // only do this if mCheckReboot is false and we have no
- // monitors, since those would need to be executed at this point.
+ // to do a context switch to check the thread. Note that we
+ // only do this if we have no monitors since those would need to
+ // be executed at this point.
mCompleted = true;
return;
}
-
if (!mCompleted) {
// we already have a check in flight, so no need
return;
@@ -236,6 +238,28 @@ public class Watchdog extends Thread {
mCurrentMonitor = null;
}
}
+
+ /** Pause the HandlerChecker. */
+ public void pauseLocked(String reason) {
+ mPauseCount++;
+ // Mark as completed, because there's a chance we called this after the watchog
+ // thread loop called Object#wait after 'WAITED_HALF'. In that case we want to ensure
+ // the next call to #getCompletionStateLocked for this checker returns 'COMPLETED'
+ mCompleted = true;
+ Slog.i(TAG, "Pausing HandlerChecker: " + mName + " for reason: "
+ + reason + ". Pause count: " + mPauseCount);
+ }
+
+ /** Resume the HandlerChecker from the last {@link #pauseLocked}. */
+ public void resumeLocked(String reason) {
+ if (mPauseCount > 0) {
+ mPauseCount--;
+ Slog.i(TAG, "Resuming HandlerChecker: " + mName + " for reason: "
+ + reason + ". Pause count: " + mPauseCount);
+ } else {
+ Slog.wtf(TAG, "Already resumed HandlerChecker: " + mName);
+ }
+ }
}
final class RebootRequestReceiver extends BroadcastReceiver {
@@ -364,6 +388,51 @@ public class Watchdog extends Thread {
}
/**
+ * Pauses Watchdog action for the currently running thread. Useful before executing long running
+ * operations that could falsely trigger the watchdog. Each call to this will require a matching
+ * call to {@link #resumeWatchingCurrentThread}.
+ *
+ * <p>If the current thread has not been added to the Watchdog, this call is a no-op.
+ *
+ * <p>If the Watchdog is already paused for the current thread, this call adds
+ * adds another pause and will require an additional {@link #resumeCurrentThread} to resume.
+ *
+ * <p>Note: Use with care, as any deadlocks on the current thread will be undetected until all
+ * pauses have been resumed.
+ */
+ public void pauseWatchingCurrentThread(String reason) {
+ synchronized (this) {
+ for (HandlerChecker hc : mHandlerCheckers) {
+ if (Thread.currentThread().equals(hc.getThread())) {
+ hc.pauseLocked(reason);
+ }
+ }
+ }
+ }
+
+ /**
+ * Resumes the last pause from {@link #pauseWatchingCurrentThread} for the currently running
+ * thread.
+ *
+ * <p>If the current thread has not been added to the Watchdog, this call is a no-op.
+ *
+ * <p>If the Watchdog action for the current thread is already resumed, this call logs a wtf.
+ *
+ * <p>If all pauses have been resumed, the Watchdog action is finally resumed, otherwise,
+ * the Watchdog action for the current thread remains paused until resume is called at least
+ * as many times as the calls to pause.
+ */
+ public void resumeWatchingCurrentThread(String reason) {
+ synchronized (this) {
+ for (HandlerChecker hc : mHandlerCheckers) {
+ if (Thread.currentThread().equals(hc.getThread())) {
+ hc.resumeLocked(reason);
+ }
+ }
+ }
+ }
+
+ /**
* Perform a full reboot of the system.
*/
void rebootSystem(String reason) {
diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java
index 376999dfd80d..beb0e4741c36 100644
--- a/services/core/java/com/android/server/am/PreBootBroadcaster.java
+++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java
@@ -107,9 +107,11 @@ public abstract class PreBootBroadcaster extends IIntentReceiver.Stub {
EventLogTags.writeAmPreBoot(mUserId, componentName.getPackageName());
mIntent.setComponent(componentName);
- mService.broadcastIntentLocked(null, null, mIntent, null, this, 0, null, null, null,
- AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID,
- Process.SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), mUserId);
+ synchronized (mService) {
+ mService.broadcastIntentLocked(null, null, mIntent, null, this, 0, null, null, null,
+ AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID,
+ Process.SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), mUserId);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 696697eddf25..b394eea95a88 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1658,23 +1658,15 @@ public final class ProcessList {
app.killed = false;
final long startSeq = app.startSeq = ++mProcStartSeqCounter;
app.setStartParams(uid, hostingRecord, seInfo, startTime);
+ app.setUsingWrapper(invokeWith != null
+ || SystemProperties.get("wrap." + app.processName) != null);
+ mPendingStarts.put(startSeq, app);
+
if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
if (DEBUG_PROCESSES) Slog.i(TAG_PROCESSES,
"Posting procStart msg for " + app.toShortString());
mService.mProcStartHandler.post(() -> {
try {
- synchronized (mService) {
- final String reason = isProcStartValidLocked(app, startSeq);
- if (reason != null) {
- Slog.w(TAG_PROCESSES, app + " not valid anymore,"
- + " don't start process, " + reason);
- app.pendingStart = false;
- return;
- }
- app.setUsingWrapper(invokeWith != null
- || SystemProperties.get("wrap." + app.processName) != null);
- mPendingStarts.put(startSeq, app);
- }
final Process.ProcessStartResult startResult = startProcess(app.hostingRecord,
entryPoint, app, app.startUid, gids, runtimeFlags, mountExternal,
app.seInfo, requiredAbi, instructionSet, invokeWith, app.startTime);
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 7605ccb5aeda..e14846863946 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -270,7 +270,7 @@ public class AttentionManagerService extends SystemService {
return;
}
if (!userState.mCurrentAttentionCheck.mCallbackInternal.equals(callbackInternal)) {
- Slog.e(LOG_TAG, "Cannot cancel a non-current request");
+ Slog.w(LOG_TAG, "Cannot cancel a non-current request");
return;
}
cancel(userState);
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 88919df04f00..b9a6a1020a77 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -760,7 +760,6 @@ public class BiometricService extends SystemService {
@Override // Binder call
public int canAuthenticate(String opPackageName) {
checkPermission();
- checkAppOp(opPackageName, Binder.getCallingUid());
final int userId = UserHandle.getCallingUserId();
final long ident = Binder.clearCallingIdentity();
@@ -833,9 +832,9 @@ public class BiometricService extends SystemService {
}
private void checkPermission() {
- if (getContext().checkCallingPermission(USE_FINGERPRINT)
+ if (getContext().checkCallingOrSelfPermission(USE_FINGERPRINT)
!= PackageManager.PERMISSION_GRANTED) {
- getContext().enforceCallingPermission(USE_BIOMETRIC,
+ getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC,
"Must have USE_BIOMETRIC permission");
}
}
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 44379264e570..3de2537cffc7 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -209,6 +209,7 @@ public class KeepaliveTracker {
case NOT_STARTED : return "NOT_STARTED";
case STARTING : return "STARTING";
case STARTED : return "STARTED";
+ case STOPPING : return "STOPPING";
}
throw new IllegalArgumentException("Unknown state");
}
@@ -326,6 +327,8 @@ public class KeepaliveTracker {
Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name());
switch (mType) {
case TYPE_NATT:
+ mNai.asyncChannel.sendMessage(
+ CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */, mPacket);
mNai.asyncChannel
.sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket);
break;
@@ -336,9 +339,8 @@ public class KeepaliveTracker {
handleStopKeepalive(mNai, mSlot, ERROR_INVALID_SOCKET);
return;
}
- mNai.asyncChannel
- .sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */,
- mPacket);
+ mNai.asyncChannel.sendMessage(
+ CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */, mPacket);
// TODO: check result from apf and notify of failure as needed.
mNai.asyncChannel
.sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket);
@@ -362,18 +364,30 @@ public class KeepaliveTracker {
Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
}
}
- if (NOT_STARTED != mStartedState) {
- mStartedState = STOPPING;
- Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name());
- if (mType == TYPE_NATT) {
- mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
- } else if (mType == TYPE_TCP) {
- mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
- mNai.asyncChannel.sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, mSlot);
- mTcpController.stopSocketMonitor(mSlot);
- } else {
- Log.wtf(TAG, "Stopping keepalive with unknown type: " + mType);
- }
+ Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name() + ": " + reason);
+ switch (mStartedState) {
+ case NOT_STARTED:
+ // Remove the reference of the keepalive that meet error before starting,
+ // e.g. invalid parameter.
+ cleanupStoppedKeepalive(mNai, mSlot);
+ break;
+ case STOPPING:
+ // Keepalive is already in stopping state, ignore.
+ return;
+ default:
+ mStartedState = STOPPING;
+ switch (mType) {
+ case TYPE_TCP:
+ mTcpController.stopSocketMonitor(mSlot);
+ // fall through
+ case TYPE_NATT:
+ mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
+ mNai.asyncChannel.sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER,
+ mSlot);
+ break;
+ default:
+ Log.wtf(TAG, "Stopping keepalive with unknown type: " + mType);
+ }
}
// Close the duplicated fd that maintains the lifecycle of socket whenever
@@ -448,14 +462,18 @@ public class KeepaliveTracker {
}
public void handleStopAllKeepalives(NetworkAgentInfo nai, int reason) {
- HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
+ final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
if (networkKeepalives != null) {
- for (KeepaliveInfo ki : networkKeepalives.values()) {
+ final ArrayList<KeepaliveInfo> kalist = new ArrayList(networkKeepalives.values());
+ for (KeepaliveInfo ki : kalist) {
ki.stop(reason);
+ // Clean up keepalives since the network agent is disconnected and unable to pass
+ // back asynchronous result of stop().
+ cleanupStoppedKeepalive(nai, ki.mSlot);
}
- networkKeepalives.clear();
- mKeepalives.remove(nai);
}
+ // Clean up keepalives will be done as a result of calling ki.stop() after the slots are
+ // freed.
}
public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
@@ -471,8 +489,24 @@ public class KeepaliveTracker {
return;
}
ki.stop(reason);
+ // Clean up keepalives will be done as a result of calling ki.stop() after the slots are
+ // freed.
+ }
+
+ private void cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot) {
+ String networkName = (nai == null) ? "(null)" : nai.name();
+ HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
+ if (networkKeepalives == null) {
+ Log.e(TAG, "Attempt to remove keepalive on nonexistent network " + networkName);
+ return;
+ }
+ KeepaliveInfo ki = networkKeepalives.get(slot);
+ if (ki == null) {
+ Log.e(TAG, "Attempt to remove nonexistent keepalive " + slot + " on " + networkName);
+ return;
+ }
networkKeepalives.remove(slot);
- Log.d(TAG, "Stopped keepalive " + slot + " on " + networkName + ", "
+ Log.d(TAG, "Remove keepalive " + slot + " on " + networkName + ", "
+ networkKeepalives.size() + " remains.");
if (networkKeepalives.isEmpty()) {
mKeepalives.remove(nai);
@@ -543,10 +577,11 @@ public class KeepaliveTracker {
handleStopKeepalive(nai, slot, reason);
}
} else if (KeepaliveInfo.STOPPING == ki.mStartedState) {
- // The message indicated result of stopping : don't call handleStopKeepalive.
+ // The message indicated result of stopping : clean up keepalive slots.
Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.name()
+ " stopped: " + reason);
ki.mStartedState = KeepaliveInfo.NOT_STARTED;
+ cleanupStoppedKeepalive(nai, slot);
} else {
Log.wtf(TAG, "Event " + message.what + "," + slot + "," + reason
+ " for keepalive in wrong state: " + ki.toString());
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index f8582cd7928f..d05369e9cfa1 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -650,7 +650,7 @@ public class PermissionMonitor {
case INetd.PERMISSION_UPDATE_DEVICE_STATS:
updateStatsPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
break;
- case INetd.NO_PERMISSIONS:
+ case INetd.PERMISSION_NONE:
noPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
break;
case INetd.PERMISSION_UNINSTALLED:
@@ -676,7 +676,7 @@ public class PermissionMonitor {
ArrayUtils.convertToIntArray(updateStatsPermissionAppIds));
}
if (noPermissionAppIds.size() != 0) {
- mNetd.trafficSetNetPermForUids(INetd.NO_PERMISSIONS,
+ mNetd.trafficSetNetPermForUids(INetd.PERMISSION_NONE,
ArrayUtils.convertToIntArray(noPermissionAppIds));
}
if (uninstalledAppIds.size() != 0) {
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 7e79a12d12eb..d8b7c2eb5d4c 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -3256,14 +3256,17 @@ public class SyncManager {
}
}
- // On account add, check if there are any settings to be restored.
- for (AccountAndUser aau : mRunningAccounts) {
- if (!containsAccountAndUser(oldAccounts, aau.account, aau.userId)) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Account " + aau.account + " added, checking sync restore data");
+ if (syncTargets != null) {
+ // On account add, check if there are any settings to be restored.
+ for (AccountAndUser aau : mRunningAccounts) {
+ if (!containsAccountAndUser(oldAccounts, aau.account, aau.userId)) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Account " + aau.account
+ + " added, checking sync restore data");
+ }
+ AccountSyncSettingsBackupHelper.accountAdded(mContext, syncTargets.userId);
+ break;
}
- AccountSyncSettingsBackupHelper.accountAdded(mContext, syncTargets.userId);
- break;
}
}
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 85fb1e0f4bdf..ad81ca2ec2b1 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -775,10 +775,10 @@ public final class ColorDisplayService extends SystemService {
final ContentResolver cr = getContext().getContentResolver();
if (isAccessibilityEnabled()) {
// There are restrictions on the available color modes combined with a11y transforms.
- if (isColorModeAvailable(COLOR_MODE_SATURATED)) {
- return COLOR_MODE_SATURATED;
- } else if (isColorModeAvailable(COLOR_MODE_AUTOMATIC)) {
- return COLOR_MODE_AUTOMATIC;
+ final int a11yColorMode = getContext().getResources().getInteger(
+ R.integer.config_accessibilityColorMode);
+ if (a11yColorMode >= 0) {
+ return a11yColorMode;
}
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index b676618f4cd3..35a82aef51b5 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -49,6 +49,7 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.net.Uri;
@@ -1625,6 +1626,15 @@ public class JobSchedulerService extends com.android.server.SystemService
}
/**
+ * Maximum time buffer in which JobScheduler will try to optimize periodic job scheduling. This
+ * does not cause a job's period to be larger than requested (eg: if the requested period is
+ * shorter than this buffer). This is used to put a limit on when JobScheduler will intervene
+ * and try to optimize scheduling if the current job finished less than this amount of time to
+ * the start of the next period
+ */
+ private static final long PERIODIC_JOB_WINDOW_BUFFER = 30 * MINUTE_IN_MILLIS;
+
+ /**
* Called after a periodic has executed so we can reschedule it. We take the last execution
* time of the job to be the time of completion (i.e. the time at which this function is
* called).
@@ -1644,16 +1654,18 @@ public class JobSchedulerService extends com.android.server.SystemService
final long period = periodicToReschedule.getJob().getIntervalMillis();
final long latestRunTimeElapsed = periodicToReschedule.getOriginalLatestRunTimeElapsed();
final long flex = periodicToReschedule.getJob().getFlexMillis();
+ long rescheduleBuffer = 0;
+ final long diffMs = Math.abs(elapsedNow - latestRunTimeElapsed);
if (elapsedNow > latestRunTimeElapsed) {
// The job ran past its expected run window. Have it count towards the current window
// and schedule a new job for the next window.
if (DEBUG) {
Slog.i(TAG, "Periodic job ran after its intended window.");
}
- final long diffMs = (elapsedNow - latestRunTimeElapsed);
int numSkippedWindows = (int) (diffMs / period) + 1; // +1 to include original window
- if (period != flex && diffMs > Math.min(30 * MINUTE_IN_MILLIS, (period - flex) / 2)) {
+ if (period != flex && diffMs > Math.min(PERIODIC_JOB_WINDOW_BUFFER,
+ (period - flex) / 2)) {
if (DEBUG) {
Slog.d(TAG, "Custom flex job ran too close to next window.");
}
@@ -1664,9 +1676,15 @@ public class JobSchedulerService extends com.android.server.SystemService
newLatestRuntimeElapsed = latestRunTimeElapsed + (period * numSkippedWindows);
} else {
newLatestRuntimeElapsed = latestRunTimeElapsed + period;
+ if (diffMs < PERIODIC_JOB_WINDOW_BUFFER && diffMs < period / 6) {
+ // Add a little buffer to the start of the next window so the job doesn't run
+ // too soon after this completed one.
+ rescheduleBuffer = Math.min(PERIODIC_JOB_WINDOW_BUFFER, period / 6 - diffMs);
+ }
}
- final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
+ final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed
+ - Math.min(flex, period - rescheduleBuffer);
if (DEBUG) {
Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
@@ -2764,12 +2782,12 @@ public class JobSchedulerService extends com.android.server.SystemService
}
@Override
- public List<JobInfo> getAllPendingJobs() throws RemoteException {
+ public ParceledListSlice<JobInfo> getAllPendingJobs() throws RemoteException {
final int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
- return JobSchedulerService.this.getPendingJobs(uid);
+ return new ParceledListSlice<>(JobSchedulerService.this.getPendingJobs(uid));
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2905,7 +2923,7 @@ public class JobSchedulerService extends com.android.server.SystemService
* <p class="note">This is a slow operation, so it should be called sparingly.
*/
@Override
- public List<JobSnapshot> getAllJobSnapshots() {
+ public ParceledListSlice<JobSnapshot> getAllJobSnapshots() {
final int uid = Binder.getCallingUid();
if (uid != Process.SYSTEM_UID) {
throw new SecurityException(
@@ -2916,7 +2934,7 @@ public class JobSchedulerService extends com.android.server.SystemService
mJobs.forEachJob((job) -> snapshots.add(
new JobSnapshot(job.getJob(), job.getSatisfiedConstraintFlags(),
isReadyToBeExecutedLocked(job))));
- return snapshots;
+ return new ParceledListSlice<>(snapshots);
}
}
};
diff --git a/services/core/java/com/android/server/location/GnssConfiguration.java b/services/core/java/com/android/server/location/GnssConfiguration.java
index bd6662d9dc51..aa51aec216eb 100644
--- a/services/core/java/com/android/server/location/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/GnssConfiguration.java
@@ -317,8 +317,10 @@ class GnssConfiguration {
if (configManager == null) {
return;
}
- PersistableBundle configs = configManager.getConfigForSubId(
- SubscriptionManager.getDefaultDataSubscriptionId());
+
+ int ddSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+ PersistableBundle configs = SubscriptionManager.isValidSubscriptionId(ddSubId)
+ ? configManager.getConfigForSubId(ddSubId) : null;
if (configs == null) {
if (DEBUG) Log.d(TAG, "SIM not ready, use default carrier config.");
configs = CarrierConfigManager.getDefaultConfig();
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 5b7eca6e310e..b2315c7b7314 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -60,7 +60,6 @@ import android.os.WorkSource.WorkChain;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
-import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
@@ -75,6 +74,7 @@ import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.internal.location.gnssmetrics.GnssMetrics;
+import com.android.internal.telephony.TelephonyIntents;
import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback;
import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback;
@@ -184,7 +184,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
private static final int DOWNLOAD_PSDS_DATA = 6;
private static final int UPDATE_LOCATION = 7; // Handle external location from network listener
private static final int DOWNLOAD_PSDS_DATA_FINISHED = 11;
- private static final int SUBSCRIPTION_OR_CARRIER_CONFIG_CHANGED = 12;
private static final int INITIALIZE_HANDLER = 13;
private static final int REQUEST_LOCATION = 16;
private static final int REPORT_LOCATION = 17; // HAL reports location
@@ -484,22 +483,13 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
updateLowPowerMode();
break;
case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
+ case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
subscriptionOrCarrierConfigChanged(context);
break;
}
}
};
- // TODO: replace OnSubscriptionsChangedListener with ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED
- // broadcast receiver.
- private final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
- new OnSubscriptionsChangedListener() {
- @Override
- public void onSubscriptionsChanged() {
- sendMessage(SUBSCRIPTION_OR_CARRIER_CONFIG_CHANGED, 0, null);
- }
- };
-
/**
* Implements {@link GnssSatelliteBlacklistCallback#onUpdateSatelliteBlacklist}.
*/
@@ -515,12 +505,15 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
mContext.getSystemService(Context.TELEPHONY_SERVICE);
CarrierConfigManager configManager = (CarrierConfigManager)
mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- String mccMnc = phone.getSimOperator();
+ int ddSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+ String mccMnc = SubscriptionManager.isValidSubscriptionId(ddSubId)
+ ? phone.getSimOperator(ddSubId) : phone.getSimOperator();
boolean isKeepLppProfile = false;
if (!TextUtils.isEmpty(mccMnc)) {
if (DEBUG) Log.d(TAG, "SIM MCC/MNC is available: " + mccMnc);
if (configManager != null) {
- PersistableBundle b = configManager.getConfig();
+ PersistableBundle b = SubscriptionManager.isValidSubscriptionId(ddSubId)
+ ? configManager.getConfigForSubId(ddSubId) : null;
if (b != null) {
isKeepLppProfile =
b.getBoolean(CarrierConfigManager.Gps.KEY_PERSIST_LPP_MODE_BOOL);
@@ -539,7 +532,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
SystemProperties.set(GnssConfiguration.LPP_PROFILE, "");
}
reloadGpsProperties();
- mNIHandler.setSuplEsEnabled(mSuplEsEnabled);
} else {
if (DEBUG) Log.d(TAG, "SIM MCC/MNC is still not available");
}
@@ -577,9 +569,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
mC2KServerPort = mGnssConfiguration.getC2KPort(TCP_MIN_PORT);
mNIHandler.setEmergencyExtensionSeconds(mGnssConfiguration.getEsExtensionSec());
mSuplEsEnabled = mGnssConfiguration.getSuplEs(0) == 1;
+ mNIHandler.setSuplEsEnabled(mSuplEsEnabled);
if (mGnssVisibilityControl != null) {
- mGnssVisibilityControl.updateProxyApps(mGnssConfiguration.getProxyApps());
- mGnssVisibilityControl.setEsNotify(mGnssConfiguration.getEsNotify(0));
+ mGnssVisibilityControl.onConfigurationUpdated(mGnssConfiguration);
}
}
@@ -1892,28 +1884,34 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
TelephonyManager phone = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
int type = AGPS_SETID_TYPE_NONE;
- String data = "";
+ String setId = null;
+ int ddSubId = SubscriptionManager.getDefaultDataSubscriptionId();
if ((flags & AGPS_RIL_REQUEST_SETID_IMSI) == AGPS_RIL_REQUEST_SETID_IMSI) {
- String data_temp = phone.getSubscriberId();
- if (data_temp == null) {
- // This means the framework does not have the SIM card ready.
- } else {
+ if (SubscriptionManager.isValidSubscriptionId(ddSubId)) {
+ setId = phone.getSubscriberId(ddSubId);
+ }
+ if (setId == null) {
+ setId = phone.getSubscriberId();
+ }
+ if (setId != null) {
// This means the framework has the SIM card.
- data = data_temp;
type = AGPS_SETID_TYPE_IMSI;
}
} else if ((flags & AGPS_RIL_REQUEST_SETID_MSISDN) == AGPS_RIL_REQUEST_SETID_MSISDN) {
- String data_temp = phone.getLine1Number();
- if (data_temp == null) {
- // This means the framework does not have the SIM card ready.
- } else {
+ if (SubscriptionManager.isValidSubscriptionId(ddSubId)) {
+ setId = phone.getLine1Number(ddSubId);
+ }
+ if (setId == null) {
+ setId = phone.getLine1Number();
+ }
+ if (setId != null) {
// This means the framework has the SIM card.
- data = data_temp;
type = AGPS_SETID_TYPE_MSISDN;
}
}
- native_agps_set_id(type, data);
+
+ native_agps_set_id(type, (setId == null) ? "" : setId);
}
@NativeEntryPoint
@@ -2025,9 +2023,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
case UPDATE_LOCATION:
handleUpdateLocation((Location) msg.obj);
break;
- case SUBSCRIPTION_OR_CARRIER_CONFIG_CHANGED:
- subscriptionOrCarrierConfigChanged(mContext);
- break;
case INITIALIZE_HANDLER:
handleInitialize();
break;
@@ -2066,17 +2061,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
// (this configuration might change in the future based on SIM changes)
reloadGpsProperties();
- // TODO: When this object "finishes" we should unregister by invoking
- // SubscriptionManager.getInstance(mContext).unregister
- // (mOnSubscriptionsChangedListener);
- // This is not strictly necessary because it will be unregistered if the
- // notification fails but it is good form.
-
- // Register for SubscriptionInfo list changes which is guaranteed
- // to invoke onSubscriptionsChanged the first time.
- SubscriptionManager.from(mContext)
- .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
-
// listen for events
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ALARM_WAKEUP);
@@ -2086,6 +2070,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ intentFilter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
mNetworkConnectivityHandler.registerNetworkCallbacks();
@@ -2164,8 +2149,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
return "DOWNLOAD_PSDS_DATA_FINISHED";
case UPDATE_LOCATION:
return "UPDATE_LOCATION";
- case SUBSCRIPTION_OR_CARRIER_CONFIG_CHANGED:
- return "SUBSCRIPTION_OR_CARRIER_CONFIG_CHANGED";
case INITIALIZE_HANDLER:
return "INITIALIZE_HANDLER";
case REPORT_LOCATION:
diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java
index a3670a45d9cb..c49d9000c1dd 100644
--- a/services/core/java/com/android/server/location/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java
@@ -76,7 +76,7 @@ class GnssVisibilityControl {
private final GpsNetInitiatedHandler mNiHandler;
private boolean mIsGpsEnabled;
- private volatile boolean mEsNotify;
+ private boolean mEsNotify;
// Number of non-framework location access proxy apps is expected to be small (< 5).
private static final int ARRAY_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS = 7;
@@ -124,10 +124,6 @@ class GnssVisibilityControl {
}
}
- void updateProxyApps(List<String> nfwLocationAccessProxyApps) {
- runOnHandler(() -> handleUpdateProxyApps(nfwLocationAccessProxyApps));
- }
-
void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
String otherProtocolStackName, byte requestor, String requestorId, byte responseType,
boolean inEmergencyMode, boolean isCachedLocation) {
@@ -136,15 +132,25 @@ class GnssVisibilityControl {
requestor, requestorId, responseType, inEmergencyMode, isCachedLocation)));
}
- void setEsNotify(int esNotifyConfig) {
- if (esNotifyConfig != ES_NOTIFY_NONE && esNotifyConfig != ES_NOTIFY_ALL) {
+ void onConfigurationUpdated(GnssConfiguration configuration) {
+ // The configuration object must be accessed only in the caller thread and not in mHandler.
+ List<String> nfwLocationAccessProxyApps = configuration.getProxyApps();
+ int esNotify = configuration.getEsNotify(ES_NOTIFY_NONE);
+ runOnHandler(() -> {
+ setEsNotify(esNotify);
+ handleUpdateProxyApps(nfwLocationAccessProxyApps);
+ });
+ }
+
+ private void setEsNotify(int esNotify) {
+ if (esNotify != ES_NOTIFY_NONE && esNotify != ES_NOTIFY_ALL) {
Log.e(TAG, "Config parameter " + GnssConfiguration.CONFIG_ES_NOTIFY_INT
- + " is set to invalid value: " + esNotifyConfig
+ + " is set to invalid value: " + esNotify
+ ". Using default value: " + ES_NOTIFY_NONE);
- esNotifyConfig = ES_NOTIFY_NONE;
+ esNotify = ES_NOTIFY_NONE;
}
- mEsNotify = (esNotifyConfig == ES_NOTIFY_ALL);
+ mEsNotify = (esNotify == ES_NOTIFY_ALL);
}
private void handleInitialize() {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index c2125b0a997d..b246eb6d38bd 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -421,8 +421,9 @@ public class LockSettingsService extends ILockSettings.Stub {
new PasswordSlotManager());
}
- public boolean hasBiometrics() {
- return BiometricManager.hasBiometrics(mContext);
+ public boolean hasEnrolledBiometrics() {
+ BiometricManager bm = mContext.getSystemService(BiometricManager.class);
+ return bm.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS;
}
public int binderGetCallingUid() {
@@ -2502,7 +2503,8 @@ public class LockSettingsService extends ILockSettings.Stub {
// TODO: When lockout is handled under the HAL for all biometrics (fingerprint),
// we need to generate challenge for each one, have it signed by GK and reset lockout
// for each modality.
- if (!hasChallenge && pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ if (!hasChallenge && pm.hasSystemFeature(PackageManager.FEATURE_FACE)
+ && mInjector.hasEnrolledBiometrics()) {
challenge = mContext.getSystemService(FaceManager.class).generateChallenge();
}
@@ -2544,8 +2546,8 @@ public class LockSettingsService extends ILockSettings.Stub {
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
notifyActivePasswordMetricsAvailable(credentialType, userCredential, userId);
unlockKeystore(authResult.authToken.deriveKeyStorePassword(), userId);
- // Reset lockout
- if (mInjector.hasBiometrics()) {
+ // Reset lockout only if user has enrolled templates
+ if (mInjector.hasEnrolledBiometrics()) {
BiometricManager bm = mContext.getSystemService(BiometricManager.class);
Slog.i(TAG, "Resetting lockout, length: "
+ authResult.gkResponse.getPayload().length);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index bab612d3c092..9d115963423d 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -73,6 +73,7 @@ public class Installer extends SystemService {
public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE;
public static final int FLAG_STORAGE_CE = IInstalld.FLAG_STORAGE_CE;
+ public static final int FLAG_STORAGE_EXTERNAL = IInstalld.FLAG_STORAGE_EXTERNAL;
public static final int FLAG_CLEAR_CACHE_ONLY = IInstalld.FLAG_CLEAR_CACHE_ONLY;
public static final int FLAG_CLEAR_CODE_CACHE_ONLY = IInstalld.FLAG_CLEAR_CODE_CACHE_ONLY;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9d702093120e..23aa8f0f13ca 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -89,6 +89,7 @@ import static android.content.pm.PackageParser.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
+import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
@@ -3249,7 +3250,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
// No apps are running this early, so no need to freeze
clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE
+ FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
}
@@ -3502,8 +3503,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
return false;
}
- clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE
- | FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
+ | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
mDexManager.notifyPackageUpdated(pkg.packageName,
pkg.baseCodePath, pkg.splitCodePaths);
}
@@ -15975,6 +15976,9 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mInstallLock) {
// Clean up both app data and code
// All package moves are frozen until finished
+
+ // We purposefully exclude FLAG_STORAGE_EXTERNAL here, since
+ // this task was only focused on moving data on internal storage.
for (int userId : userIds) {
try {
mInstaller.destroyAppData(volumeUuid, move.packageName, userId,
@@ -17075,8 +17079,8 @@ public class PackageManagerService extends IPackageManager.Stub
final String packageName = pkg.packageName;
prepareAppDataAfterInstallLIF(pkg);
if (reconciledPkg.prepareResult.clearCodeCache) {
- clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
- | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
+ | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
if (reconciledPkg.prepareResult.replace) {
mDexManager.notifyPackageUpdated(pkg.packageName,
@@ -18848,7 +18852,7 @@ public class PackageManagerService extends IPackageManager.Stub
resolvedPkg.setVolumeUuid(deletedPs.volumeUuid);
}
destroyAppDataLIF(resolvedPkg, UserHandle.USER_ALL,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+ FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
destroyAppProfilesLIF(resolvedPkg);
if (outInfo != null) {
outInfo.dataRemoved = true;
@@ -19600,7 +19604,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
destroyAppDataLIF(pkg, nextUserId,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+ FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
clearDefaultBrowserIfNeededForUser(ps.name, nextUserId);
removeKeystoreDataIfNeeded(nextUserId, ps.appId);
final SparseBooleanArray changedUsers = new SparseBooleanArray();
@@ -19736,7 +19740,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
clearAppDataLIF(pkg, userId,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+ FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
removeKeystoreDataIfNeeded(userId, appId);
@@ -19967,8 +19971,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (doClearData) {
synchronized (mInstallLock) {
- final int flags = StorageManager.FLAG_STORAGE_DE
- | StorageManager.FLAG_STORAGE_CE;
+ final int flags = FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL;
// We're only clearing cache files, so we don't care if the
// app is unfrozen and still able to run
clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CACHE_ONLY);
@@ -22516,9 +22519,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (!Build.FINGERPRINT.equals(ver.fingerprint)) {
- clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE
- | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ clearAppDataLIF(ps.pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
+ | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
}
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 37c1aaa45b8b..b2ba2904cabc 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -929,6 +929,7 @@ public class PermissionManagerService {
final BasePermission bp = mSettings.getPermissionLocked(permName);
final boolean appSupportsRuntimePermissions =
pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M;
+ String upgradedActivityRecognitionPermission = null;
if (DEBUG_INSTALL) {
Log.i(TAG, "Package " + pkg.packageName + " checking " + permName + ": " + bp);
@@ -947,11 +948,40 @@ public class PermissionManagerService {
// Cache newImplicitPermissions before modifing permissionsState as for the shared
// uids the original and new state are the same object
if (!origPermissions.hasRequestedPermission(permName)
- && pkg.implicitPermissions.contains(permName)) {
- newImplicitPermissions.add(permName);
+ && (pkg.implicitPermissions.contains(permName)
+ || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
+ if (pkg.implicitPermissions.contains(permName)) {
+ // If permName is an implicit permission, try to auto-grant
+ newImplicitPermissions.add(permName);
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, permName + " is newly added for " + pkg.packageName);
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, permName + " is newly added for " + pkg.packageName);
+ }
+ } else {
+ // Special case for Activity Recognition permission. Even if AR permission
+ // is not an implicit permission we want to add it to the list (try to
+ // auto-grant it) if the app was installed on a device before AR permission
+ // was split, regardless of if the app now requests the new AR permission
+ // or has updated its target SDK and AR is no longer implicit to it.
+ // This is a compatibility workaround for apps when AR permission was
+ // split in Q.
+ int numSplitPerms = PermissionManager.SPLIT_PERMISSIONS.size();
+ for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
+ PermissionManager.SplitPermissionInfo sp =
+ PermissionManager.SPLIT_PERMISSIONS.get(splitPermNum);
+ String splitPermName = sp.getSplitPermission();
+ if (sp.getNewPermissions().contains(permName)
+ && origPermissions.hasInstallPermission(splitPermName)) {
+ upgradedActivityRecognitionPermission = splitPermName;
+ newImplicitPermissions.add(permName);
+
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, permName + " is newly added for "
+ + pkg.packageName);
+ }
+ break;
+ }
+ }
}
}
@@ -985,7 +1015,8 @@ public class PermissionManagerService {
// For all apps normal permissions are install time ones.
grant = GRANT_INSTALL;
} else if (bp.isRuntime()) {
- if (origPermissions.hasInstallPermission(bp.getName())) {
+ if (origPermissions.hasInstallPermission(bp.getName())
+ || upgradedActivityRecognitionPermission != null) {
// Before Q we represented some runtime permissions as install permissions,
// in Q we cannot do this anymore. Hence upgrade them all.
grant = GRANT_UPGRADE;
@@ -1161,10 +1192,15 @@ public class PermissionManagerService {
.getInstallPermissionState(perm);
int flags = (permState != null) ? permState.getFlags() : 0;
+ BasePermission bpToRevoke =
+ upgradedActivityRecognitionPermission == null
+ ? bp : mSettings.getPermissionLocked(
+ upgradedActivityRecognitionPermission);
// Remove install permission
- if (origPermissions.revokeInstallPermission(bp)
+ if (origPermissions.revokeInstallPermission(bpToRevoke)
!= PERMISSION_OPERATION_FAILURE) {
- origPermissions.updatePermissionFlags(bp, UserHandle.USER_ALL,
+ origPermissions.updatePermissionFlags(bpToRevoke,
+ UserHandle.USER_ALL,
(MASK_PERMISSION_FLAGS_ALL
& ~FLAG_PERMISSION_APPLY_RESTRICTION), 0);
changedInstallPermission = true;
@@ -1489,9 +1525,11 @@ public class PermissionManagerService {
for (int userNum = 0; userNum < numUsers; userNum++) {
int userId = users[userNum];
- ps.updatePermissionFlags(bp, userId,
- FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
- FLAG_PERMISSION_REVOKE_WHEN_REQUESTED);
+ if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) {
+ ps.updatePermissionFlags(bp, userId,
+ FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
+ FLAG_PERMISSION_REVOKE_WHEN_REQUESTED);
+ }
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
boolean inheritsFromInstallPerm = false;
diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java
index 3262eb67a052..14f1196ab3a2 100644
--- a/services/core/java/com/android/server/power/AttentionDetector.java
+++ b/services/core/java/com/android/server/power/AttentionDetector.java
@@ -75,6 +75,8 @@ public class AttentionDetector {
*/
private final AtomicBoolean mRequested;
+ private long mLastActedOnNextScreenDimming;
+
/**
* Monotonously increasing ID for the requests sent.
*/
@@ -150,6 +152,9 @@ public class AttentionDetector {
}
public long updateUserActivity(long nextScreenDimming) {
+ if (nextScreenDimming == mLastActedOnNextScreenDimming) {
+ return nextScreenDimming;
+ }
if (!mIsSettingEnabled) {
return nextScreenDimming;
}
@@ -190,13 +195,14 @@ public class AttentionDetector {
// afterwards if AttentionManager couldn't deliver it.
mRequested.set(true);
mRequestId++;
+ mLastActedOnNextScreenDimming = nextScreenDimming;
mCallback = new AttentionCallbackInternalImpl(mRequestId);
+ Slog.v(TAG, "Checking user attention, ID: " + mRequestId);
final boolean sent = mAttentionManager.checkAttention(getAttentionTimeout(), mCallback);
if (!sent) {
mRequested.set(false);
}
- Slog.v(TAG, "Checking user attention, ID: " + mRequestId);
return whenToCheck;
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 1edb93a0cdb9..7d0da68a0750 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -15,6 +15,7 @@
*/
package com.android.server.stats;
+import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
import static android.os.Process.getPidsForCommands;
@@ -33,6 +34,11 @@ import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.HistoricalOps;
+import android.app.AppOpsManager.HistoricalOpsRequest;
+import android.app.AppOpsManager.HistoricalPackageOps;
+import android.app.AppOpsManager.HistoricalUidOps;
import android.app.ProcessMemoryHighWaterMark;
import android.app.ProcessMemoryState;
import android.app.StatsManager;
@@ -146,6 +152,8 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -154,6 +162,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -1941,6 +1950,53 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
+ private void pullAppOps(long elapsedNanos, final long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+
+ CompletableFuture<HistoricalOps> ops = new CompletableFuture<>();
+ HistoricalOpsRequest histOpsRequest =
+ new HistoricalOpsRequest.Builder(
+ Instant.now().minus(1, ChronoUnit.HOURS).toEpochMilli(),
+ Long.MAX_VALUE).build();
+ appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete);
+
+ HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
+ TimeUnit.MILLISECONDS);
+
+ StatsLogEventWrapper e = new StatsLogEventWrapper(StatsLog.APP_OPS, elapsedNanos,
+ wallClockNanos);
+
+ for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) {
+ final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx);
+ final int uid = uidOps.getUid();
+ for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) {
+ final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx);
+ for (int opIdx = 0; opIdx < packageOps.getOpCount(); opIdx++) {
+ final AppOpsManager.HistoricalOp op = packageOps.getOpAt(opIdx);
+ e.writeInt(uid);
+ e.writeString(packageOps.getPackageName());
+ e.writeInt(op.getOpCode());
+ e.writeLong(op.getForegroundAccessCount(OP_FLAGS_ALL_TRUSTED));
+ e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_ALL_TRUSTED));
+ e.writeLong(op.getForegroundRejectCount(OP_FLAGS_ALL_TRUSTED));
+ e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_ALL_TRUSTED));
+ e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_ALL_TRUSTED));
+ e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_ALL_TRUSTED));
+ pulledData.add(e);
+ }
+ }
+ }
+ } catch (Throwable t) {
+ Log.e(TAG, "Could not read appops", t);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+
/**
* Add a RoleHolder atom for each package that holds a role.
*
@@ -2331,6 +2387,10 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
pullFaceSettings(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+ case StatsLog.APP_OPS: {
+ pullAppOps(elapsedNanos, wallClockNanos, ret);
+ break;
+ }
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index ba415861acc6..3d59e66d13ef 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -281,8 +281,9 @@ class ActivityStack extends ConfigurationContainer {
if (display != null && inSplitScreenPrimaryWindowingMode()) {
// If we created a docked stack we want to resize it so it resizes all other stacks
// in the system.
- getStackDockedModeBounds(null, null, mTmpRect2, mTmpRect3);
- mStackSupervisor.resizeDockedStackLocked(getRequestedOverrideBounds(), mTmpRect2,
+ getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
+ mTmpRect /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
+ mStackSupervisor.resizeDockedStackLocked(getRequestedOverrideBounds(), mTmpRect,
mTmpRect2, null, null, PRESERVE_WINDOWS);
}
mRootActivityContainer.updateUIDsPresentOnDisplay();
@@ -396,7 +397,6 @@ class ActivityStack extends ConfigurationContainer {
private final Rect mTmpRect = new Rect();
private final Rect mTmpRect2 = new Rect();
- private final Rect mTmpRect3 = new Rect();
private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
/** List for processing through a set of activities */
@@ -512,7 +512,6 @@ class ActivityStack extends ConfigurationContainer {
mWindowManager = mService.mWindowManager;
mStackId = stackId;
mCurrentUser = mService.mAmInternal.getCurrentUserId();
- mTmpRect2.setEmpty();
// Set display id before setting activity and window type to make sure it won't affect
// stacks on a wrong display.
mDisplayId = display.mDisplayId;
@@ -572,90 +571,87 @@ class ActivityStack extends ConfigurationContainer {
public void onConfigurationChanged(Configuration newParentConfig) {
final int prevWindowingMode = getWindowingMode();
final boolean prevIsAlwaysOnTop = isAlwaysOnTop();
- final ActivityDisplay display = getDisplay();
final int prevRotation = getWindowConfiguration().getRotation();
final int prevDensity = getConfiguration().densityDpi;
final int prevScreenW = getConfiguration().screenWidthDp;
final int prevScreenH = getConfiguration().screenHeightDp;
-
- getBounds(mTmpRect); // previous bounds
+ final Rect newBounds = mTmpRect;
+ // Initialize the new bounds by previous bounds as the input and output for calculating
+ // override bounds in pinned (pip) or split-screen mode.
+ getBounds(newBounds);
super.onConfigurationChanged(newParentConfig);
- if (display == null) {
- return;
- }
- if (getTaskStack() == null) {
+ final ActivityDisplay display = getDisplay();
+ if (display == null || getTaskStack() == null) {
return;
}
+ final boolean windowingModeChanged = prevWindowingMode != getWindowingMode();
+ final int overrideWindowingMode = getRequestedOverrideWindowingMode();
// Update bounds if applicable
boolean hasNewOverrideBounds = false;
// Use override windowing mode to prevent extra bounds changes if inheriting the mode.
- if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_PINNED) {
+ if (overrideWindowingMode == WINDOWING_MODE_PINNED) {
// Pinned calculation already includes rotation
- mTmpRect2.set(mTmpRect);
- hasNewOverrideBounds = getTaskStack().calculatePinnedBoundsForConfigChange(mTmpRect2);
- } else {
+ hasNewOverrideBounds = getTaskStack().calculatePinnedBoundsForConfigChange(newBounds);
+ } else if (!matchParentBounds()) {
+ // If the parent (display) has rotated, rotate our bounds to best-fit where their
+ // bounds were on the pre-rotated display.
final int newRotation = getWindowConfiguration().getRotation();
- if (!matchParentBounds()) {
- // If the parent (display) has rotated, rotate our bounds to best-fit where their
- // bounds were on the pre-rotated display.
- if (prevRotation != newRotation) {
- mTmpRect2.set(mTmpRect);
- getDisplay().mDisplayContent
- .rotateBounds(newParentConfig.windowConfiguration.getBounds(),
- prevRotation, newRotation, mTmpRect2);
- hasNewOverrideBounds = true;
- }
+ final boolean rotationChanged = prevRotation != newRotation;
+ if (rotationChanged) {
+ display.mDisplayContent.rotateBounds(
+ newParentConfig.windowConfiguration.getBounds(), prevRotation, newRotation,
+ newBounds);
+ hasNewOverrideBounds = true;
+ }
+ // Use override windowing mode to prevent extra bounds changes if inheriting the mode.
+ if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
// If entering split screen or if something about the available split area changes,
// recalculate the split windows to match the new configuration.
- if (prevRotation != newRotation
+ if (rotationChanged || windowingModeChanged
|| prevDensity != getConfiguration().densityDpi
- || prevWindowingMode != getWindowingMode()
|| prevScreenW != getConfiguration().screenWidthDp
|| prevScreenH != getConfiguration().screenHeightDp) {
- // Use override windowing mode to prevent extra bounds changes if inheriting
- // the mode.
- if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || getRequestedOverrideWindowingMode()
- == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- mTmpRect2.set(mTmpRect);
- getTaskStack()
- .calculateDockedBoundsForConfigChange(newParentConfig, mTmpRect2);
- hasNewOverrideBounds = true;
- }
+ getTaskStack().calculateDockedBoundsForConfigChange(newParentConfig, newBounds);
+ hasNewOverrideBounds = true;
}
}
}
- if (getWindowingMode() != prevWindowingMode) {
+
+ if (windowingModeChanged) {
// Use override windowing mode to prevent extra bounds changes if inheriting the mode.
- if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- getStackDockedModeBounds(null, null, mTmpRect2, mTmpRect3);
+ if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
+ newBounds /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
// immediately resize so docked bounds are available in onSplitScreenModeActivated
setTaskDisplayedBounds(null);
- setTaskBounds(mTmpRect2);
- setBounds(mTmpRect2);
- } else if (
- getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ setTaskBounds(newBounds);
+ setBounds(newBounds);
+ newBounds.set(newBounds);
+ } else if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
Rect dockedBounds = display.getSplitScreenPrimaryStack().getBounds();
final boolean isMinimizedDock =
- getDisplay().mDisplayContent.getDockedDividerController().isMinimizedDock();
+ display.mDisplayContent.getDockedDividerController().isMinimizedDock();
if (isMinimizedDock) {
TaskRecord topTask = display.getSplitScreenPrimaryStack().topTask();
if (topTask != null) {
dockedBounds = topTask.getBounds();
}
}
- getStackDockedModeBounds(dockedBounds, null, mTmpRect2, mTmpRect3);
+ getStackDockedModeBounds(dockedBounds, null /* currentTempTaskBounds */,
+ newBounds /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
hasNewOverrideBounds = true;
}
- }
- if (prevWindowingMode != getWindowingMode()) {
display.onStackWindowingModeChanged(this);
}
if (hasNewOverrideBounds) {
- mRootActivityContainer.resizeStack(this, mTmpRect2, null, null, PRESERVE_WINDOWS,
+ // Note the resizeStack may enter onConfigurationChanged recursively, so we make a copy
+ // of the temporary bounds (newBounds is mTmpRect) to avoid it being modified.
+ mRootActivityContainer.resizeStack(this, new Rect(newBounds), null /* tempTaskBounds */,
+ null /* tempTaskInsetBounds */, PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, true /* deferResume */);
}
if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index b8442a887dac..1cdb49d25dfd 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -46,7 +46,6 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
-import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -304,7 +303,7 @@ class ActivityStartInterceptor {
return null;
}
// TODO(b/28935539): should allow certain activities to bypass work challenge
- final IntentSender target = createIntentSenderForOriginalIntent(Binder.getCallingUid(),
+ final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE);
final KeyguardManager km = (KeyguardManager) mServiceContext
.getSystemService(KEYGUARD_SERVICE);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index fd90f0339e63..ecbecbafd3d5 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -2005,7 +2005,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
}
layoutLetterbox(winHint);
if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
- mLetterbox.applySurfaceChanges(mPendingTransaction);
+ mLetterbox.applySurfaceChanges(getPendingTransaction());
}
}
@@ -3059,13 +3059,13 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
if (mSurfaceControl != null) {
if (show && !mLastSurfaceShowing) {
- mPendingTransaction.show(mSurfaceControl);
+ getPendingTransaction().show(mSurfaceControl);
} else if (!show && mLastSurfaceShowing) {
- mPendingTransaction.hide(mSurfaceControl);
+ getPendingTransaction().hide(mSurfaceControl);
}
}
if (mThumbnail != null) {
- mThumbnail.setShowing(mPendingTransaction, show);
+ mThumbnail.setShowing(getPendingTransaction(), show);
}
mLastSurfaceShowing = show;
super.prepareSurfaces();
@@ -3225,8 +3225,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
private void updateColorTransform() {
if (mSurfaceControl != null && mLastAppSaturationInfo != null) {
- mPendingTransaction.setColorTransform(mSurfaceControl, mLastAppSaturationInfo.mMatrix,
- mLastAppSaturationInfo.mTranslation);
+ getPendingTransaction().setColorTransform(mSurfaceControl,
+ mLastAppSaturationInfo.mMatrix, mLastAppSaturationInfo.mTranslation);
mWmService.scheduleAnimationLocked();
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 58a03b26b959..82ea4fe42799 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1480,7 +1480,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// the top of the method, the caller is obligated to call computeNewConfigurationLocked().
// By updating the Display info here it will be available to
// #computeScreenConfiguration() later.
- updateDisplayAndOrientation(getConfiguration().uiMode);
+ updateDisplayAndOrientation(getConfiguration().uiMode, null /* outConfig */);
// NOTE: We disable the rotation in the emulator because
// it doesn't support hardware OpenGL emulation yet.
@@ -1578,7 +1578,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* changed.
* Do not call if {@link WindowManagerService#mDisplayReady} == false.
*/
- private DisplayInfo updateDisplayAndOrientation(int uiMode) {
+ private DisplayInfo updateDisplayAndOrientation(int uiMode, Configuration outConfig) {
// Use the effective "visual" dimensions based on current rotation
final boolean rotated = (mRotation == ROTATION_90 || mRotation == ROTATION_270);
final int dw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
@@ -1610,6 +1610,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mDisplayInfo.flags &= ~Display.FLAG_SCALING_DISABLED;
}
+ computeSizeRangesAndScreenLayout(mDisplayInfo, rotated, uiMode, dw, dh,
+ mDisplayMetrics.density, outConfig);
+
// We usually set the override info in DisplayManager so that we get consistent display
// metrics values when displays are changing and don't send out new values until WM is aware
// of them. However, we don't do this for displays that serve as containers for ActivityView
@@ -1658,7 +1661,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* Do not call if mDisplayReady == false.
*/
void computeScreenConfiguration(Configuration config) {
- final DisplayInfo displayInfo = updateDisplayAndOrientation(config.uiMode);
+ final DisplayInfo displayInfo = updateDisplayAndOrientation(config.uiMode, config);
calculateBounds(displayInfo, mTmpBounds);
config.windowConfiguration.setBounds(mTmpBounds);
@@ -1688,9 +1691,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final boolean rotated = (displayInfo.rotation == Surface.ROTATION_90
|| displayInfo.rotation == Surface.ROTATION_270);
- computeSizeRangesAndScreenLayout(displayInfo, rotated, config.uiMode, dw, dh, density,
- config);
-
config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK)
| ((displayInfo.flags & Display.FLAG_ROUND) != 0
? Configuration.SCREENLAYOUT_ROUND_YES
@@ -1844,6 +1844,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_90, uiMode, unrotDh, unrotDw);
adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_180, uiMode, unrotDw, unrotDh);
adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_270, uiMode, unrotDh, unrotDw);
+
+ if (outConfig == null) {
+ return;
+ }
int sl = Configuration.resetScreenLayout(outConfig.screenLayout);
sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode,
displayInfo.displayCutout);
@@ -3340,7 +3344,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final SurfaceControl newParent =
shouldAttachToDisplay ? mWindowingLayer : computeImeParent();
if (newParent != null) {
- mPendingTransaction.reparent(mImeWindowsContainers.mSurfaceControl, newParent);
+ getPendingTransaction().reparent(mImeWindowsContainers.mSurfaceControl, newParent);
scheduleAnimation();
}
}
@@ -3747,7 +3751,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mPortalWindowHandle.touchableRegion.getBounds(mTmpRect);
if (!mTmpBounds.equals(mTmpRect)) {
mPortalWindowHandle.touchableRegion.set(mTmpBounds);
- mPendingTransaction.setInputWindowInfo(mParentSurfaceControl, mPortalWindowHandle);
+ getPendingTransaction().setInputWindowInfo(
+ mParentSurfaceControl, mPortalWindowHandle);
}
}
}
@@ -4846,18 +4851,23 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
try {
final ScreenRotationAnimation screenRotationAnimation =
mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+ final Transaction transaction = getPendingTransaction();
if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
screenRotationAnimation.getEnterTransformation().getMatrix().getValues(mTmpFloats);
- mPendingTransaction.setMatrix(mWindowingLayer,
+ transaction.setMatrix(mWindowingLayer,
mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
- mPendingTransaction.setPosition(mWindowingLayer,
+ transaction.setPosition(mWindowingLayer,
mTmpFloats[Matrix.MTRANS_X], mTmpFloats[Matrix.MTRANS_Y]);
- mPendingTransaction.setAlpha(mWindowingLayer,
+ transaction.setAlpha(mWindowingLayer,
screenRotationAnimation.getEnterTransformation().getAlpha());
}
super.prepareSurfaces();
+
+ // TODO: Once we totally eliminate global transaction we will pass transaction in here
+ // rather than merging to global.
+ SurfaceControl.mergeToGlobalTransaction(transaction);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -5013,7 +5023,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
if (mPortalWindowHandle == null) {
mPortalWindowHandle = createPortalWindowHandle(sc.toString());
}
- mPendingTransaction.setInputWindowInfo(sc, mPortalWindowHandle)
+ getPendingTransaction().setInputWindowInfo(sc, mPortalWindowHandle)
.reparent(mWindowingLayer, sc).reparent(mOverlayLayer, sc);
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index b8504db8e810..197a3cf2b3ab 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -255,6 +255,7 @@ public class DisplayPolicy {
private volatile boolean mNavigationBarCanMove;
private volatile boolean mNavigationBarLetsThroughTaps;
private volatile boolean mNavigationBarAlwaysShowOnSideGesture;
+ private volatile boolean mAllowSeamlessRotationDespiteNavBarMoving;
// Written by vr manager thread, only read in this class.
private volatile boolean mPersistentVrModeEnabled;
@@ -2726,6 +2727,8 @@ public class DisplayPolicy {
mNavigationBarCanMove =
mDisplayContent.mBaseDisplayWidth != mDisplayContent.mBaseDisplayHeight
&& res.getBoolean(R.bool.config_navBarCanMove);
+ mAllowSeamlessRotationDespiteNavBarMoving =
+ res.getBoolean(R.bool.config_allowSeamlessRotationDespiteNavBarMoving);
}
/**
@@ -3508,8 +3511,9 @@ public class DisplayPolicy {
}
// If the navigation bar can't change sides, then it will
// jump when we change orientations and we don't rotate
- // seamlessly.
- if (!navigationBarCanMove()) {
+ // seamlessly - unless that is allowed, eg. with gesture
+ // navigation where the navbar is low-profile enough that this isn't very noticeable.
+ if (!navigationBarCanMove() && !mAllowSeamlessRotationDespiteNavBarMoving) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index bb7867ce53ec..38201069ebca 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -138,6 +138,7 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
setOrientation(SCREEN_ORIENTATION_UNSET);
}
+ @Override
DisplayContent getDisplayContent() {
return mStack != null ? mStack.getDisplayContent() : null;
}
diff --git a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
index 17e4b897c9da..ee4e462cb85e 100644
--- a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
+++ b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
@@ -77,7 +77,7 @@ class TaskScreenshotAnimatable implements SurfaceAnimator.Animatable {
@Override
public SurfaceControl.Transaction getPendingTransaction() {
- return mTask.mPendingTransaction;
+ return mTask.getPendingTransaction();
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 757f6a1c2f94..ab5e071f572a 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -93,10 +93,6 @@ public class TaskStack extends WindowContainer<Task> implements
/** Unique identifier */
final int mStackId;
- /** The display this stack sits under. */
- // TODO: Track parent marks like this in WindowContainer.
- private DisplayContent mDisplayContent;
-
/** For comparison with DisplayContent bounds. */
private Rect mTmpRect = new Rect();
private Rect mTmpRect2 = new Rect();
@@ -177,10 +173,6 @@ public class TaskStack extends WindowContainer<Task> implements
EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId);
}
- DisplayContent getDisplayContent() {
- return mDisplayContent;
- }
-
Task findHomeTask() {
if (!isActivityTypeHome() || mChildren.isEmpty()) {
return null;
@@ -825,8 +817,7 @@ public class TaskStack extends WindowContainer<Task> implements
throw new IllegalStateException("onDisplayChanged: Already attached");
}
- final boolean movedToNewDisplay = mDisplayContent == null;
- mDisplayContent = dc;
+ super.onDisplayChanged(dc);
updateSurfaceBounds();
if (mAnimationBackgroundSurface == null) {
@@ -834,8 +825,6 @@ public class TaskStack extends WindowContainer<Task> implements
.setName("animation background stackId=" + mStackId)
.build();
}
-
- super.onDisplayChanged(dc);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index d5c3e4f34c26..bbef261d17bb 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -109,14 +109,19 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// The owner/creator for this container. No controller if null.
WindowContainerController mController;
+ // The display this window container is on.
+ protected DisplayContent mDisplayContent;
+
protected SurfaceControl mSurfaceControl;
private int mLastLayer = 0;
private SurfaceControl mLastRelativeToLayer = null;
+ // TODO(b/132320879): Remove this from WindowContainers except DisplayContent.
+ private final Transaction mPendingTransaction;
+
/**
* Applied as part of the animation pass in "prepareSurfaces".
*/
- protected final Transaction mPendingTransaction;
protected final SurfaceAnimator mSurfaceAnimator;
protected final WindowManagerService mWmService;
@@ -320,12 +325,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
if (mSurfaceControl != null) {
- mPendingTransaction.remove(mSurfaceControl);
+ getPendingTransaction().remove(mSurfaceControl);
// Merge to parent transaction to ensure the transactions on this WindowContainer are
// applied in native even if WindowContainer is removed.
if (mParent != null) {
- mParent.getPendingTransaction().merge(mPendingTransaction);
+ mParent.getPendingTransaction().merge(getPendingTransaction());
}
mSurfaceControl = null;
@@ -508,12 +513,20 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* @param dc The display this container is on after changes.
*/
void onDisplayChanged(DisplayContent dc) {
+ mDisplayContent = dc;
+ if (dc != null && dc != this) {
+ dc.getPendingTransaction().merge(mPendingTransaction);
+ }
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer child = mChildren.get(i);
child.onDisplayChanged(dc);
}
}
+ DisplayContent getDisplayContent() {
+ return mDisplayContent;
+ }
+
void setWaitingForDrawnIfResizingChanged() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer wc = mChildren.get(i);
@@ -1180,13 +1193,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
}
- /**
- * TODO: Once we totally eliminate global transaction we will pass transaction in here
- * rather than merging to global.
- */
void prepareSurfaces() {
- SurfaceControl.mergeToGlobalTransaction(getPendingTransaction());
-
// If a leash has been set when the transaction was committed, then the leash reparent has
// been committed.
mCommittedReparentToAnimationLeash = mSurfaceAnimator.hasLeash();
@@ -1204,8 +1211,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/**
- * Trigger a call to prepareSurfaces from the animation thread, such that
- * mPendingTransaction will be applied.
+ * Trigger a call to prepareSurfaces from the animation thread, such that pending transactions
+ * will be applied.
*/
void scheduleAnimation() {
if (mParent != null) {
@@ -1224,6 +1231,14 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
@Override
public Transaction getPendingTransaction() {
+ final DisplayContent displayContent = getDisplayContent();
+ if (displayContent != null && displayContent != this) {
+ return displayContent.getPendingTransaction();
+ }
+ // This WindowContainer has not attached to a display yet or this is a DisplayContent, so we
+ // let the caller to save the surface operations within the local mPendingTransaction.
+ // If this is not a DisplayContent, we will merge it to the pending transaction of its
+ // display once it attaches to it.
return mPendingTransaction;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index dd3c6004dcad..5ef184adc52f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1313,6 +1313,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mOrientationChangeTimedOut = true;
}
+ @Override
DisplayContent getDisplayContent() {
return mToken.getDisplayContent();
}
@@ -4602,7 +4603,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */,
0 /* windowCornerRadius */),
mWmService.mSurfaceAnimationRunner);
- startAnimation(mPendingTransaction, adapter);
+ startAnimation(getPendingTransaction(), adapter);
commitPendingTransaction();
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index f0b9c62f2843..8aee0f2a8308 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -82,9 +82,6 @@ class WindowToken extends WindowContainer<WindowState> {
// windows will be put to the bottom of the list.
boolean sendingToBottom;
- // The display this token is on.
- protected DisplayContent mDisplayContent;
-
/** The owner has {@link android.Manifest.permission#MANAGE_APP_TOKENS} */
final boolean mOwnerCanManageAppTokens;
@@ -249,10 +246,6 @@ class WindowToken extends WindowContainer<WindowState> {
return null;
}
- DisplayContent getDisplayContent() {
- return mDisplayContent;
- }
-
@Override
void removeImmediately() {
if (mDisplayContent != null) {
@@ -266,7 +259,6 @@ class WindowToken extends WindowContainer<WindowState> {
@Override
void onDisplayChanged(DisplayContent dc) {
dc.reParentWindowToken(this);
- mDisplayContent = dc;
// TODO(b/36740756): One day this should perhaps be hooked
// up with goodToGo, so we don't move a window
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 204a1ea977e7..fb3076ba9ddd 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -59,6 +59,7 @@
#include <android_view_PointerIcon.h>
#include <android/graphics/GraphicsJNI.h>
+#include <nativehelper/ScopedLocalFrame.h>
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
@@ -723,6 +724,7 @@ nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApp
ATRACE_CALL();
JNIEnv* env = jniEnv();
+ ScopedLocalFrame localFrame(env);
jobject tokenObj = javaObjectForIBinder(env, token);
jstring reasonObj = env->NewStringUTF(reason.c_str());
@@ -735,8 +737,6 @@ nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApp
} else {
assert(newTimeout >= 0);
}
-
- env->DeleteLocalRef(reasonObj);
return newTimeout;
}
@@ -747,6 +747,7 @@ void NativeInputManager::notifyInputChannelBroken(const sp<IBinder>& token) {
ATRACE_CALL();
JNIEnv* env = jniEnv();
+ ScopedLocalFrame localFrame(env);
jobject tokenObj = javaObjectForIBinder(env, token);
if (tokenObj) {
@@ -764,6 +765,7 @@ void NativeInputManager::notifyFocusChanged(const sp<IBinder>& oldToken,
ATRACE_CALL();
JNIEnv* env = jniEnv();
+ ScopedLocalFrame localFrame(env);
jobject oldTokenObj = javaObjectForIBinder(env, oldToken);
jobject newTokenObj = javaObjectForIBinder(env, newToken);
@@ -1139,6 +1141,7 @@ nsecs_t NativeInputManager::interceptKeyBeforeDispatching(
nsecs_t result = 0;
if (policyFlags & POLICY_FLAG_TRUSTED) {
JNIEnv* env = jniEnv();
+ ScopedLocalFrame localFrame(env);
// Token may be null
jobject tokenObj = javaObjectForIBinder(env, token);
@@ -1173,6 +1176,7 @@ bool NativeInputManager::dispatchUnhandledKey(const sp<IBinder>& token,
bool result = false;
if (policyFlags & POLICY_FLAG_TRUSTED) {
JNIEnv* env = jniEnv();
+ ScopedLocalFrame localFrame(env);
// Note: tokenObj may be null.
jobject tokenObj = javaObjectForIBinder(env, token);
@@ -1224,6 +1228,7 @@ bool NativeInputManager::checkInjectEventsPermissionNonReentrant(
void NativeInputManager::onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) {
ATRACE_CALL();
JNIEnv* env = jniEnv();
+ ScopedLocalFrame localFrame(env);
jobject touchedTokenObj = javaObjectForIBinder(env, touchedToken);
env->CallVoidMethod(mServiceObj, gServiceClassInfo.onPointerDownOutsideFocus, touchedTokenObj);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8c65fa8b53bf..e42ed3b03139 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1049,12 +1049,7 @@ public final class SystemServer {
mDisplayManagerService.windowManagerAndInputReady();
traceEnd();
- // Skip Bluetooth if we have an emulator kernel
- // TODO: Use a more reliable check to see if this product should
- // support Bluetooth - see bug 988521
- if (isEmulator) {
- Slog.i(TAG, "No Bluetooth Service (emulator)");
- } else if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
+ if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
Slog.i(TAG, "No Bluetooth Service (factory test)");
} else if (!context.getPackageManager().hasSystemFeature
(PackageManager.FEATURE_BLUETOOTH)) {
@@ -1167,9 +1162,12 @@ public final class SystemServer {
if (!mOnlyCore) {
traceBeginAndSlog("UpdatePackagesIfNeeded");
try {
+ Watchdog.getInstance().pauseWatchingCurrentThread("dexopt");
mPackageManagerService.updatePackagesIfNeeded();
} catch (Throwable e) {
reportWtf("update packages", e);
+ } finally {
+ Watchdog.getInstance().resumeWatchingCurrentThread("dexopt");
}
traceEnd();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index f7edf65a499f..18c524ad7a94 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -183,15 +183,188 @@ public class JobSchedulerServiceTest {
assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
- advanceElapsedClock(45 * MINUTE_IN_MILLIS); // now + 55 minutes
+ advanceElapsedClock(20 * MINUTE_IN_MILLIS); // now + 30 minutes
rescheduledJob = mService.getRescheduleJobForPeriodic(job);
assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+ advanceElapsedClock(25 * MINUTE_IN_MILLIS); // now + 55 minutes
+
+ rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ // Shifted because it's close to the end of the window.
+ assertEquals(nextWindowStartTime + 5 * MINUTE_IN_MILLIS,
+ rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
advanceElapsedClock(4 * MINUTE_IN_MILLIS); // now + 59 minutes
rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ // Shifted because it's close to the end of the window.
+ assertEquals(nextWindowStartTime + 9 * MINUTE_IN_MILLIS,
+ rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+ }
+
+ /**
+ * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+ * with an extra delay and correct deadline constraint if the periodic job is completed near the
+ * end of its expected running window.
+ */
+ @Test
+ public void testGetRescheduleJobForPeriodic_closeToEndOfWindow() {
+ JobStatus frequentJob = createJobStatus(
+ "testGetRescheduleJobForPeriodic_closeToEndOfWindow",
+ createJobInfo().setPeriodic(15 * MINUTE_IN_MILLIS));
+ long now = sElapsedRealtimeClock.millis();
+ long nextWindowStartTime = now + 15 * MINUTE_IN_MILLIS;
+ long nextWindowEndTime = now + 30 * MINUTE_IN_MILLIS;
+
+ // At the beginning of the window. Next window should be unaffected.
+ JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // Halfway through window. Next window should be unaffected.
+ advanceElapsedClock((long) (7.5 * MINUTE_IN_MILLIS));
+ rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // In last 1/6 of window. Next window start time should be shifted slightly.
+ advanceElapsedClock(6 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(frequentJob);
+ assertEquals(nextWindowStartTime + MINUTE_IN_MILLIS,
+ rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ JobStatus mediumJob = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow",
+ createJobInfo().setPeriodic(HOUR_IN_MILLIS));
+ now = sElapsedRealtimeClock.millis();
+ nextWindowStartTime = now + HOUR_IN_MILLIS;
+ nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
+
+ // At the beginning of the window. Next window should be unaffected.
+ rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // Halfway through window. Next window should be unaffected.
+ advanceElapsedClock(30 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // At the edge 1/6 of window. Next window should be unaffected.
+ advanceElapsedClock(20 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // In last 1/6 of window. Next window start time should be shifted slightly.
+ advanceElapsedClock(6 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(mediumJob);
+ assertEquals(nextWindowStartTime + (6 * MINUTE_IN_MILLIS),
+ rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ JobStatus longJob = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow",
+ createJobInfo().setPeriodic(6 * HOUR_IN_MILLIS));
+ now = sElapsedRealtimeClock.millis();
+ nextWindowStartTime = now + 6 * HOUR_IN_MILLIS;
+ nextWindowEndTime = now + 12 * HOUR_IN_MILLIS;
+
+ // At the beginning of the window. Next window should be unaffected.
+ rescheduledJob = mService.getRescheduleJobForPeriodic(longJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // Halfway through window. Next window should be unaffected.
+ advanceElapsedClock(3 * HOUR_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(longJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // At the edge 1/6 of window. Next window should be unaffected.
+ advanceElapsedClock(2 * HOUR_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(longJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // In last 1/6 of window. Next window should be unaffected since we're over the shift cap.
+ advanceElapsedClock(15 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(longJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // In last 1/6 of window. Next window start time should be shifted slightly.
+ advanceElapsedClock(30 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(longJob);
+ assertEquals(nextWindowStartTime + (30 * MINUTE_IN_MILLIS),
+ rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // Flex duration close to period duration.
+ JobStatus gameyFlex = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow",
+ createJobInfo().setPeriodic(HOUR_IN_MILLIS, 59 * MINUTE_IN_MILLIS));
+ now = sElapsedRealtimeClock.millis();
+ nextWindowStartTime = now + HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
+ nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
+ advanceElapsedClock(MINUTE_IN_MILLIS);
+
+ // At the beginning of the window. Next window should be unaffected.
+ rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // Halfway through window. Next window should be unaffected.
+ advanceElapsedClock(29 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // At the edge 1/6 of window. Next window should be unaffected.
+ advanceElapsedClock(20 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // In last 1/6 of window. Next window start time should be shifted slightly.
+ advanceElapsedClock(6 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(gameyFlex);
+ assertEquals(nextWindowStartTime + (5 * MINUTE_IN_MILLIS),
+ rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // Very short flex duration compared to period duration.
+ JobStatus superFlex = createJobStatus("testGetRescheduleJobForPeriodic_closeToEndOfWindow",
+ createJobInfo().setPeriodic(HOUR_IN_MILLIS, 10 * MINUTE_IN_MILLIS));
+ now = sElapsedRealtimeClock.millis();
+ nextWindowStartTime = now + HOUR_IN_MILLIS + 50 * MINUTE_IN_MILLIS;
+ nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
+ advanceElapsedClock(MINUTE_IN_MILLIS);
+
+ // At the beginning of the window. Next window should be unaffected.
+ rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // Halfway through window. Next window should be unaffected.
+ advanceElapsedClock(29 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // At the edge 1/6 of window. Next window should be unaffected.
+ advanceElapsedClock(20 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // In last 1/6 of window. Next window should be unaffected since the flex duration pushes
+ // the next window start time far enough away.
+ advanceElapsedClock(6 * MINUTE_IN_MILLIS);
+ rescheduledJob = mService.getRescheduleJobForPeriodic(superFlex);
assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
}
@@ -265,7 +438,9 @@ public class JobSchedulerServiceTest {
advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 55 minutes
rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
- assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ // Shifted because it's close to the end of the window.
+ assertEquals(nextWindowStartTime + 5 * MINUTE_IN_MILLIS,
+ rescheduledJob.getEarliestRunTime());
assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 57 minutes
@@ -273,7 +448,9 @@ public class JobSchedulerServiceTest {
advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 59 minutes
rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
- assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ // Shifted because it's close to the end of the window.
+ assertEquals(nextWindowStartTime + 9 * MINUTE_IN_MILLIS,
+ rescheduledJob.getEarliestRunTime());
assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index db3f3c25c5b6..f85e2cc10800 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -110,7 +110,7 @@ public class LockSettingsServiceTestable extends LockSettingsService {
}
@Override
- public boolean hasBiometrics() {
+ public boolean hasEnrolledBiometrics() {
return false;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 8af4edda3b8b..18453aa13264 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -27,6 +27,7 @@ import android.app.trust.TrustManager;
import android.content.pm.UserInfo;
import android.database.sqlite.SQLiteDatabase;
import android.os.FileUtils;
+import android.os.SystemClock;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.platform.test.annotations.Presubmit;
@@ -125,7 +126,7 @@ public class LockSettingsStorageTests extends AndroidTestCase {
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 100; i++) {
final int threadId = i;
- threads.add(new Thread() {
+ threads.add(new Thread("testKeyValue_Concurrency_" + i) {
@Override
public void run() {
synchronized (monitor) {
@@ -134,17 +135,17 @@ public class LockSettingsStorageTests extends AndroidTestCase {
} catch (InterruptedException e) {
return;
}
- mStorage.writeKeyValue("key", "1 from thread " + threadId, 0);
- mStorage.readKeyValue("key", "default", 0);
- mStorage.writeKeyValue("key", "2 from thread " + threadId, 0);
- mStorage.readKeyValue("key", "default", 0);
- mStorage.writeKeyValue("key", "3 from thread " + threadId, 0);
- mStorage.readKeyValue("key", "default", 0);
- mStorage.writeKeyValue("key", "4 from thread " + threadId, 0);
- mStorage.readKeyValue("key", "default", 0);
- mStorage.writeKeyValue("key", "5 from thread " + threadId, 0);
- mStorage.readKeyValue("key", "default", 0);
}
+ mStorage.writeKeyValue("key", "1 from thread " + threadId, 0);
+ mStorage.readKeyValue("key", "default", 0);
+ mStorage.writeKeyValue("key", "2 from thread " + threadId, 0);
+ mStorage.readKeyValue("key", "default", 0);
+ mStorage.writeKeyValue("key", "3 from thread " + threadId, 0);
+ mStorage.readKeyValue("key", "default", 0);
+ mStorage.writeKeyValue("key", "4 from thread " + threadId, 0);
+ mStorage.readKeyValue("key", "default", 0);
+ mStorage.writeKeyValue("key", "5 from thread " + threadId, 0);
+ mStorage.readKeyValue("key", "default", 0);
}
});
threads.get(i).start();
@@ -153,12 +154,7 @@ public class LockSettingsStorageTests extends AndroidTestCase {
synchronized (monitor) {
monitor.notifyAll();
}
- for (int i = 0; i < threads.size(); i++) {
- try {
- threads.get(i).join();
- } catch (InterruptedException e) {
- }
- }
+ joinAll(threads, 10000);
assertEquals('5', mStorage.readKeyValue("key", "default", 0).charAt(0));
mStorage.clearCache();
assertEquals('5', mStorage.readKeyValue("key", "default", 0).charAt(0));
@@ -515,4 +511,29 @@ public class LockSettingsStorageTests extends AndroidTestCase {
}
return captured[0];
}
+
+ private static void joinAll(List<Thread> threads, long timeoutMillis) {
+ long deadline = SystemClock.uptimeMillis() + timeoutMillis;
+ for (Thread t : threads) {
+ try {
+ t.join(deadline - SystemClock.uptimeMillis());
+ if (t.isAlive()) {
+ t.interrupt();
+ throw new RuntimeException(
+ "Joining " + t + " timed out. Stack: \n" + getStack(t));
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted while joining " + t, e);
+ }
+ }
+ }
+
+ private static String getStack(Thread t) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(t.toString()).append('\n');
+ for (StackTraceElement ste : t.getStackTrace()) {
+ sb.append("\tat ").append(ste.toString()).append('\n');
+ }
+ return sb.toString();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
index c30a7dd8321c..a63f49b1fe3d 100644
--- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
@@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -182,10 +183,22 @@ public class AttentionDetectorTest extends AndroidTestCase {
}
@Test
+ public void testOnUserActivity_ignoresIfAlreadyDoneForThatNextScreenDimming() {
+ long when = registerAttention();
+ verify(mAttentionManagerInternal).checkAttention(anyLong(), any());
+ assertThat(when).isLessThan(mNextDimming);
+ clearInvocations(mAttentionManagerInternal);
+
+ long redundantWhen = mAttentionDetector.updateUserActivity(mNextDimming);
+ assertThat(redundantWhen).isEqualTo(mNextDimming);
+ verify(mAttentionManagerInternal, never()).checkAttention(anyLong(), any());
+ }
+
+ @Test
public void testOnUserActivity_skipsIfAlreadyScheduled() {
registerAttention();
reset(mAttentionManagerInternal);
- long when = mAttentionDetector.updateUserActivity(mNextDimming);
+ long when = mAttentionDetector.updateUserActivity(mNextDimming + 1);
verify(mAttentionManagerInternal, never()).checkAttention(anyLong(), any());
assertThat(when).isLessThan(mNextDimming);
}
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 77866279a751..75e8fb5a34fe 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -1356,7 +1356,7 @@ public class AppStandbyController {
private void fetchCarrierPrivilegedAppsLocked() {
TelephonyManager telephonyManager =
mContext.getSystemService(TelephonyManager.class);
- mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges();
+ mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivilegesForAllPhones();
mHaveCarrierPrivilegedApps = true;
if (DEBUG) {
Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index faf6ee23e208..0f3050f9e3d3 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -120,6 +120,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
switch (vol.type) {
+ case VolumeInfo.TYPE_PUBLIC:
case VolumeInfo.TYPE_PRIVATE:
case VolumeInfo.TYPE_EMULATED:
if (newState == VolumeInfo.STATE_MOUNTED) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index e1ffb0f179f8..d77ea6e41cc2 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -77,6 +77,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.LocalServices;
+import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.soundtrigger.SoundTriggerInternal;
@@ -1283,7 +1284,9 @@ public class VoiceInteractionManagerService extends SystemService {
mRm.addOnRoleHoldersChangedListenerAsUser(executor, this, UserHandle.ALL);
UserHandle currentUser = UserHandle.of(LocalServices.getService(
ActivityManagerInternal.class).getCurrentUserId());
- onRoleHoldersChanged(RoleManager.ROLE_ASSISTANT, currentUser);
+ SystemServerInitThreadPool.get().submit(() -> onRoleHoldersChanged(
+ RoleManager.ROLE_ASSISTANT, currentUser),
+ "VoiceInteractionManagerService RoleObserver initialization");
}
private @NonNull String getDefaultRecognizer(@NonNull UserHandle user) {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 93c1b2138ccd..eefaf47d0387 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1451,6 +1451,9 @@ public class TelecomManager {
* foreground call is ended.
* <p>
* Requires permission {@link android.Manifest.permission#ANSWER_PHONE_CALLS}.
+ * <p>
+ * Note: this method CANNOT be used to end ongoing emergency calls and will return {@code false}
+ * if an attempt is made to end an emergency call.
*
* @return {@code true} if there is a call which will be rejected or terminated, {@code false}
* otherwise.
@@ -1458,8 +1461,6 @@ public class TelecomManager {
* instead. Apps performing call screening should use the {@link CallScreeningService} API
* instead.
*/
-
-
@RequiresPermission(Manifest.permission.ANSWER_PHONE_CALLS)
@Deprecated
public boolean endCall() {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index d00341b3fe92..6ba359b78ee4 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -836,13 +836,6 @@ public class CarrierConfigManager {
"carrier_metered_roaming_apn_types_strings";
/**
- * Default APN types that are metered on IWLAN by the carrier
- * @hide
- */
- public static final String KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS =
- "carrier_metered_iwlan_apn_types_strings";
-
- /**
* CDMA carrier ERI (Enhanced Roaming Indicator) file name
* @hide
*/
@@ -3116,15 +3109,6 @@ public class CarrierConfigManager {
new String[]{"default", "mms", "dun", "supl"});
sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
new String[]{"default", "mms", "dun", "supl"});
- // By default all APNs should be unmetered if the device is on IWLAN. However, we add
- // default APN as metered here as a workaround for P because in some cases, a data
- // connection was brought up on cellular, but later on the device camped on IWLAN. That
- // data connection was incorrectly treated as unmetered due to the current RAT IWLAN.
- // Marking it as metered for now can workaround the issue.
- // Todo: This will be fixed in Q when IWLAN full refactoring is completed.
- sDefaults.putStringArray(KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS,
- new String[]{"default"});
-
sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY,
new int[]{
4, /* IS95A */
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index c57f9e63f9db..f31ac2ea5f35 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -173,6 +173,11 @@ public class SubscriptionInfo implements Parcelable {
private ParcelUuid mGroupUUID;
/**
+ * A package name that specifies who created the group. Null if mGroupUUID is null.
+ */
+ private String mGroupOwner;
+
+ /**
* Whether group of the subscription is disabled.
* This is only useful if it's a grouped opportunistic subscription. In this case, if all
* primary (non-opportunistic) subscriptions in the group are deactivated (unplugged pSIM
@@ -203,9 +208,10 @@ public class SubscriptionInfo implements Parcelable {
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
@Nullable UiccAccessRule[] accessRules, String cardString) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
- roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString,
- false, null, TelephonyManager.UNKNOWN_CARRIER_ID,
- SubscriptionManager.PROFILE_CLASS_DEFAULT);
+ roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString, -1,
+ false, null, false, TelephonyManager.UNKNOWN_CARRIER_ID,
+ SubscriptionManager.PROFILE_CLASS_DEFAULT,
+ SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null);
}
/**
@@ -219,7 +225,7 @@ public class SubscriptionInfo implements Parcelable {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString, -1,
isOpportunistic, groupUUID, false, carrierId, profileClass,
- SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null);
}
/**
@@ -229,8 +235,8 @@ public class SubscriptionInfo implements Parcelable {
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
@Nullable UiccAccessRule[] accessRules, String cardString, int cardId,
- boolean isOpportunistic, @Nullable String groupUUID,
- boolean isGroupDisabled, int carrierId, int profileClass, int subType) {
+ boolean isOpportunistic, @Nullable String groupUUID, boolean isGroupDisabled,
+ int carrierId, int profileClass, int subType, @Nullable String groupOwner) {
this.mId = id;
this.mIccId = iccId;
this.mSimSlotIndex = simSlotIndex;
@@ -254,6 +260,7 @@ public class SubscriptionInfo implements Parcelable {
this.mCarrierId = carrierId;
this.mProfileClass = profileClass;
this.mSubscriptionType = subType;
+ this.mGroupOwner = groupOwner;
}
/**
@@ -500,6 +507,15 @@ public class SubscriptionInfo implements Parcelable {
}
/**
+ * Return owner package of group the subscription belongs to.
+ *
+ * @hide
+ */
+ public @Nullable String getGroupOwner() {
+ return mGroupOwner;
+ }
+
+ /**
* @return the profile class of this subscription.
* @hide
*/
@@ -646,11 +662,12 @@ public class SubscriptionInfo implements Parcelable {
int subType = source.readInt();
String[] ehplmns = source.readStringArray();
String[] hplmns = source.readStringArray();
+ String groupOwner = source.readString();
SubscriptionInfo info = new SubscriptionInfo(id, iccId, simSlotIndex, displayName,
carrierName, nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc,
countryIso, isEmbedded, accessRules, cardString, cardId, isOpportunistic,
- groupUUID, isGroupDisabled, carrierid, profileClass, subType);
+ groupUUID, isGroupDisabled, carrierid, profileClass, subType, groupOwner);
info.setAssociatedPlmns(ehplmns, hplmns);
return info;
}
@@ -688,6 +705,7 @@ public class SubscriptionInfo implements Parcelable {
dest.writeInt(mSubscriptionType);
dest.writeStringArray(mEhplmns);
dest.writeStringArray(mHplmns);
+ dest.writeString(mGroupOwner);
}
@Override
@@ -727,7 +745,8 @@ public class SubscriptionInfo implements Parcelable {
+ " profileClass=" + mProfileClass
+ " ehplmns = " + Arrays.toString(mEhplmns)
+ " hplmns = " + Arrays.toString(mHplmns)
- + " subscriptionType=" + mSubscriptionType + "}";
+ + " subscriptionType=" + mSubscriptionType
+ + " mGroupOwner=" + mGroupOwner + "}";
}
@Override
@@ -735,7 +754,7 @@ public class SubscriptionInfo implements Parcelable {
return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
mIsOpportunistic, mGroupUUID, mIccId, mNumber, mMcc, mMnc,
mCountryIso, mCardString, mCardId, mDisplayName, mCarrierName, mAccessRules,
- mIsGroupDisabled, mCarrierId, mProfileClass);
+ mIsGroupDisabled, mCarrierId, mProfileClass, mGroupOwner);
}
@Override
@@ -767,6 +786,7 @@ public class SubscriptionInfo implements Parcelable {
&& Objects.equals(mCountryIso, toCompare.mCountryIso)
&& Objects.equals(mCardString, toCompare.mCardString)
&& Objects.equals(mCardId, toCompare.mCardId)
+ && Objects.equals(mGroupOwner, toCompare.mGroupOwner)
&& TextUtils.equals(mDisplayName, toCompare.mDisplayName)
&& TextUtils.equals(mCarrierName, toCompare.mCarrierName)
&& Arrays.equals(mAccessRules, toCompare.mAccessRules)
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 32105ad52753..addd9e0591b0 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -396,6 +396,12 @@ public class SubscriptionManager {
public static final int NAME_SOURCE_USER_INPUT = 2;
/**
+ * The name_source is carrier (carrier app, carrier config, etc.)
+ * @hide
+ */
+ public static final int NAME_SOURCE_CARRIER = 3;
+
+ /**
* TelephonyProvider column name for the color of a SIM.
* <P>Type: INTEGER (int)</P>
*/
@@ -686,6 +692,15 @@ public class SubscriptionManager {
* @hide
*/
public static final String GROUP_UUID = "group_uuid";
+
+ /**
+ * TelephonyProvider column name for group owner. It's the package name who created
+ * the subscription group.
+ *
+ * @hide
+ */
+ public static final String GROUP_OWNER = "group_owner";
+
/**
* TelephonyProvider column name for whether a subscription is metered or not, that is, whether
* the network it connects to charges for subscription or not. For example, paid CBRS or unpaid.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 328a0a7a9512..dab1e6f4abde 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7475,7 +7475,7 @@ public class TelephonyManager {
try {
ITelephony telephony = getITelephony();
if (telephony != null)
- return telephony.checkCarrierPrivilegesForPackage(pkgName);
+ return telephony.checkCarrierPrivilegesForPackage(getSubId(), pkgName);
} catch (RemoteException ex) {
Rlog.e(TAG, "checkCarrierPrivilegesForPackage RemoteException", ex);
} catch (NullPointerException ex) {
@@ -7526,7 +7526,7 @@ public class TelephonyManager {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getPackagesWithCarrierPrivileges();
+ return telephony.getPackagesWithCarrierPrivileges(getPhoneId());
}
} catch (RemoteException ex) {
Rlog.e(TAG, "getPackagesWithCarrierPrivileges RemoteException", ex);
@@ -7537,6 +7537,22 @@ public class TelephonyManager {
}
/** @hide */
+ public List<String> getPackagesWithCarrierPrivilegesForAllPhones() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getPackagesWithCarrierPrivilegesForAllPhones();
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getPackagesWithCarrierPrivilegesForAllPhones RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "getPackagesWithCarrierPrivilegesForAllPhones NPE", ex);
+ }
+ return Collections.EMPTY_LIST;
+ }
+
+
+ /** @hide */
@SystemApi
@SuppressLint("Doclava125")
public void dial(String number) {
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index a86fda4454d7..a78bae4514fb 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1273,6 +1273,23 @@ public class ApnSetting implements Parcelable {
}
/**
+ * Get supported APN types
+ *
+ * @return list of APN types
+ * @hide
+ */
+ @ApnType
+ public List<Integer> getApnTypes() {
+ List<Integer> types = new ArrayList<>();
+ for (Integer type : APN_TYPE_INT_MAP.keySet()) {
+ if ((mApnTypeBitmask & type) == type) {
+ types.add(type);
+ }
+ }
+ return types;
+ }
+
+ /**
* @param apnTypeBitmask bitmask of APN types.
* @return comma delimited list of APN types.
* @hide
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index e8ce2b457ab4..68fd9ac8d6bd 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1000,7 +1000,7 @@ interface ITelephony {
/**
* Similar to above, but check for the package whose name is pkgName.
*/
- int checkCarrierPrivilegesForPackage(String pkgName);
+ int checkCarrierPrivilegesForPackage(int subId, String pkgName);
/**
* Similar to above, but check across all phones.
@@ -1357,9 +1357,14 @@ interface ITelephony {
in PhoneAccountHandle phoneAccountHandle, boolean enabled);
/**
- * Returns a list of packages that have carrier privileges.
+ * Returns a list of packages that have carrier privileges for the specific phone.
*/
- List<String> getPackagesWithCarrierPrivileges();
+ List<String> getPackagesWithCarrierPrivileges(int phoneId);
+
+ /**
+ * Returns a list of packages that have carrier privileges.
+ */
+ List<String> getPackagesWithCarrierPrivilegesForAllPhones();
/**
* Return the application ID for the app type.
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index fe1378736e68..b0cc20785cbf 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -4342,8 +4342,9 @@ public class ConnectivityServiceTest {
}
// Check that there is no port leaked after all keepalives and sockets are closed.
- assertFalse(isUdpPortInUse(srcPort));
- assertFalse(isUdpPortInUse(srcPort2));
+ // TODO: enable this check after ensuring a valid free port. See b/129512753#comment7.
+ // assertFalse(isUdpPortInUse(srcPort));
+ // assertFalse(isUdpPortInUse(srcPort2));
mWiFiNetworkAgent.disconnect();
waitFor(mWiFiNetworkAgent.getDisconnectedCV());
@@ -4471,7 +4472,8 @@ public class ConnectivityServiceTest {
assertEquals(anyIPv4, sa.getAddress());
testPfd.close();
- assertFalse(isUdpPortInUse(srcPort));
+ // TODO: enable this check after ensuring a valid free port. See b/129512753#comment7.
+ // assertFalse(isUdpPortInUse(srcPort));
mWiFiNetworkAgent.disconnect();
waitFor(mWiFiNetworkAgent.getDisconnectedCV());
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 62a471896579..df1f57f7a011 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -524,7 +524,7 @@ public class PermissionMonitorTest {
SparseIntArray netdPermissionsAppIds = new SparseIntArray();
netdPermissionsAppIds.put(MOCK_UID1, INetd.PERMISSION_INTERNET);
- netdPermissionsAppIds.put(MOCK_UID2, INetd.NO_PERMISSIONS);
+ netdPermissionsAppIds.put(MOCK_UID2, INetd.PERMISSION_NONE);
netdPermissionsAppIds.put(SYSTEM_UID1, INetd.PERMISSION_INTERNET
| INetd.PERMISSION_UPDATE_DEVICE_STATS);
netdPermissionsAppIds.put(SYSTEM_UID2, INetd.PERMISSION_UPDATE_DEVICE_STATS);
@@ -534,7 +534,7 @@ public class PermissionMonitorTest {
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET,
new int[]{MOCK_UID1});
- mNetdServiceMonitor.expectPermission(INetd.NO_PERMISSIONS, new int[]{MOCK_UID2});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{MOCK_UID2});
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
| INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{SYSTEM_UID1});
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UPDATE_DEVICE_STATS,
@@ -553,8 +553,8 @@ public class PermissionMonitorTest {
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{SYSTEM_UID2});
// Revoke permission from SYSTEM_UID1, expect no permission stored.
- mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID1, INetd.NO_PERMISSIONS);
- mNetdServiceMonitor.expectPermission(INetd.NO_PERMISSIONS, new int[]{SYSTEM_UID1});
+ mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID1, INetd.PERMISSION_NONE);
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{SYSTEM_UID1});
}
private PackageInfo addPackage(String packageName, int uid, String[] permissions)
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 0b1108d56b7f..c0c0361dd92f 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -160,6 +160,11 @@ public class ScanResult implements Parcelable {
public static final int KEY_MGMT_FT_SAE = 11;
/**
* @hide
+ * Security key management scheme: OWE in transition mode.
+ */
+ public static final int KEY_MGMT_OWE_TRANSITION = 12;
+ /**
+ * @hide
* No cipher suite.
*/
public static final int CIPHER_NONE = 0;