diff options
177 files changed, 3720 insertions, 1647 deletions
diff --git a/api/current.txt b/api/current.txt index 8b24826a267b..3ec7f447e8b9 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"; @@ -219,12 +220,6 @@ package android { public static final class R.attr { ctor public R.attr(); - field public static final int __removed1 = 16844185; // 0x1010599 - field public static final int __removed2 = 16844186; // 0x101059a - field public static final int __removed3 = 16844187; // 0x101059b - field public static final int __removed4 = 16844188; // 0x101059c - field public static final int __removed5 = 16844189; // 0x101059d - field public static final int __removed6 = 16844182; // 0x1010596 field public static final int absListViewStyle = 16842858; // 0x101006a field public static final int accessibilityEventTypes = 16843648; // 0x1010380 field public static final int accessibilityFeedbackType = 16843650; // 0x1010382 @@ -283,7 +278,7 @@ package android { field public static final int alertDialogTheme = 16843529; // 0x1010309 field public static final int alignmentMode = 16843642; // 0x101037a field public static final int allContactsName = 16843468; // 0x10102cc - field public static final int allowAudioPlaybackCapture = 16844199; // 0x10105a7 + field public static final int allowAudioPlaybackCapture = 16844289; // 0x1010601 field public static final int allowBackup = 16843392; // 0x1010280 field public static final int allowClearUserData = 16842757; // 0x1010005 field public static final int allowEmbedded = 16843765; // 0x10103f5 @@ -571,8 +566,8 @@ package android { field public static final int endX = 16844050; // 0x1010512 field public static final int endY = 16844051; // 0x1010513 field @Deprecated public static final int endYear = 16843133; // 0x101017d - field public static final int enforceNavigationBarContrast = 16844203; // 0x10105ab - field public static final int enforceStatusBarContrast = 16844202; // 0x10105aa + field public static final int enforceNavigationBarContrast = 16844293; // 0x1010605 + field public static final int enforceStatusBarContrast = 16844292; // 0x1010604 field public static final int enterFadeDuration = 16843532; // 0x101030c field public static final int entries = 16842930; // 0x10100b2 field public static final int entryValues = 16843256; // 0x10101f8 @@ -645,10 +640,10 @@ package android { field public static final int footerDividersEnabled = 16843311; // 0x101022f field public static final int forceDarkAllowed = 16844172; // 0x101058c field public static final int forceHasOverlappingRendering = 16844065; // 0x1010521 - field public static final int forceUriPermissions = 16844197; // 0x10105a5 + field public static final int forceUriPermissions = 16844191; // 0x101059f field public static final int foreground = 16843017; // 0x1010109 field public static final int foregroundGravity = 16843264; // 0x1010200 - field public static final int foregroundServiceType = 16844191; // 0x101059f + field public static final int foregroundServiceType = 16844185; // 0x1010599 field public static final int foregroundTint = 16843885; // 0x101046d field public static final int foregroundTintMode = 16843886; // 0x101046e field public static final int format = 16843013; // 0x1010105 @@ -706,7 +701,7 @@ package android { field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e field public static final int hardwareAccelerated = 16843475; // 0x10102d3 field public static final int hasCode = 16842764; // 0x101000c - field public static final int hasFragileUserData = 16844192; // 0x10105a0 + field public static final int hasFragileUserData = 16844186; // 0x101059a field @Deprecated public static final int headerAmPmTextAppearance = 16843936; // 0x10104a0 field public static final int headerBackground = 16843055; // 0x101012f field @Deprecated public static final int headerDayOfMonthTextAppearance = 16843927; // 0x1010497 @@ -734,7 +729,7 @@ package android { field public static final int iconTintMode = 16844127; // 0x101055f field public static final int iconifiedByDefault = 16843514; // 0x10102fa field public static final int id = 16842960; // 0x10100d0 - field public static final int identifier = 16844204; // 0x10105ac + field public static final int identifier = 16844294; // 0x1010606 field public static final int ignoreGravity = 16843263; // 0x10101ff field public static final int imageButtonStyle = 16842866; // 0x1010072 field public static final int imageWellStyle = 16842867; // 0x1010073 @@ -766,7 +761,7 @@ package android { field public static final int indicatorRight = 16843022; // 0x101010e field public static final int indicatorStart = 16843729; // 0x10103d1 field public static final int inflatedId = 16842995; // 0x10100f3 - field public static final int inheritShowWhenLocked = 16844194; // 0x10105a2 + field public static final int inheritShowWhenLocked = 16844188; // 0x101059c field public static final int initOrder = 16842778; // 0x101001a field public static final int initialKeyguardLayout = 16843714; // 0x10103c2 field public static final int initialLayout = 16843345; // 0x1010251 @@ -943,7 +938,7 @@ package android { field public static final int menuCategory = 16843230; // 0x10101de field public static final int mimeType = 16842790; // 0x1010026 field public static final int min = 16844089; // 0x1010539 - field public static final int minAspectRatio = 16844193; // 0x10105a1 + field public static final int minAspectRatio = 16844187; // 0x101059b field public static final int minDate = 16843583; // 0x101033f field public static final int minEms = 16843098; // 0x101015a field public static final int minHeight = 16843072; // 0x1010140 @@ -1124,7 +1119,7 @@ package android { field public static final int reqKeyboardType = 16843304; // 0x1010228 field public static final int reqNavigation = 16843306; // 0x101022a field public static final int reqTouchScreen = 16843303; // 0x1010227 - field public static final int requestLegacyExternalStorage = 16844201; // 0x10105a9 + field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603 field public static final int requireDeviceUnlock = 16843756; // 0x10103ec field public static final int required = 16843406; // 0x101028e field public static final int requiredAccountType = 16843734; // 0x10103d6 @@ -1203,7 +1198,7 @@ package android { field public static final int secondaryProgress = 16843064; // 0x1010138 field public static final int secondaryProgressTint = 16843879; // 0x1010467 field public static final int secondaryProgressTintMode = 16843880; // 0x1010468 - field public static final int secureElementName = 16844200; // 0x10105a8 + field public static final int secureElementName = 16844290; // 0x1010602 field public static final int seekBarStyle = 16842875; // 0x101007b field public static final int segmentedButtonStyle = 16843568; // 0x1010330 field public static final int selectAllOnFocus = 16843102; // 0x101015e @@ -1212,7 +1207,7 @@ package android { field public static final int selectableItemBackgroundBorderless = 16843868; // 0x101045c field @Deprecated public static final int selectedDateVerticalBar = 16843591; // 0x1010347 field @Deprecated public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342 - field public static final int selectionDividerHeight = 16844190; // 0x101059e + field public static final int selectionDividerHeight = 16844184; // 0x1010598 field public static final int sessionService = 16843837; // 0x101043d field public static final int settingsActivity = 16843301; // 0x1010225 field public static final int settingsSliceUri = 16844179; // 0x1010593 @@ -1331,7 +1326,7 @@ package android { field public static final int supportsAssist = 16844016; // 0x10104f0 field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1 field public static final int supportsLocalInteraction = 16844047; // 0x101050f - field public static final int supportsMultipleDisplays = 16844183; // 0x1010597 + field public static final int supportsMultipleDisplays = 16844182; // 0x1010596 field public static final int supportsPictureInPicture = 16844023; // 0x10104f7 field public static final int supportsRtl = 16843695; // 0x10103af field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb @@ -1512,9 +1507,9 @@ package android { field public static final int unselectedAlpha = 16843278; // 0x101020e field public static final int updatePeriodMillis = 16843344; // 0x1010250 field public static final int use32bitAbi = 16844053; // 0x1010515 - field public static final int useAppZygote = 16844184; // 0x1010598 + field public static final int useAppZygote = 16844183; // 0x1010597 field public static final int useDefaultMargins = 16843641; // 0x1010379 - field public static final int useEmbeddedDex = 16844196; // 0x10105a4 + field public static final int useEmbeddedDex = 16844190; // 0x101059e field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310 field public static final int useLevel = 16843167; // 0x101019f field public static final int userVisible = 16843409; // 0x1010291 @@ -1626,7 +1621,7 @@ package android { field @Deprecated public static final int yearListSelectorColor = 16843930; // 0x101049a field public static final int yesNoPreferenceStyle = 16842896; // 0x1010090 field public static final int zAdjustment = 16843201; // 0x10101c1 - field public static final int zygotePreloadName = 16844195; // 0x10105a3 + field public static final int zygotePreloadName = 16844189; // 0x101059d } public static final class R.bool { @@ -10326,6 +10321,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"; @@ -34164,7 +34160,7 @@ package android.os { field public static final int O = 26; // 0x1a field public static final int O_MR1 = 27; // 0x1b field public static final int P = 28; // 0x1c - field public static final int Q = 10000; // 0x2710 + field public static final int Q = 29; // 0x1d } public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable { @@ -56249,12 +56245,10 @@ package android.widget { method public int getWindowLayoutType(); method public boolean isAboveAnchor(); method public boolean isAttachedInDecor(); - method @Deprecated public boolean isClipToScreenEnabled(); method public boolean isClippedToScreen(); method public boolean isClippingEnabled(); method public boolean isFocusable(); method public boolean isLaidOutInScreen(); - method @Deprecated public boolean isLayoutInScreenEnabled(); method public boolean isOutsideTouchable(); method public boolean isShowing(); method public boolean isSplitTouchEnabled(); @@ -56263,7 +56257,6 @@ package android.widget { method public void setAnimationStyle(int); method public void setAttachedInDecor(boolean); method public void setBackgroundDrawable(android.graphics.drawable.Drawable); - method @Deprecated public void setClipToScreenEnabled(boolean); method public void setClippingEnabled(boolean); method public void setContentView(android.view.View); method public void setElevation(float); @@ -56276,7 +56269,6 @@ package android.widget { method public void setInputMethodMode(int); method public void setIsClippedToScreen(boolean); method public void setIsLaidOutInScreen(boolean); - method @Deprecated public void setLayoutInScreenEnabled(boolean); method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener); method public void setOutsideTouchable(boolean); method public void setOverlapAnchor(boolean); diff --git a/api/removed.txt b/api/removed.txt index ab5e7e5deeaa..536eba1779f2 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -691,6 +691,13 @@ package android.widget { method protected <T extends android.view.View> T findViewWithTagTraversal(Object); } + public class PopupWindow { + method @Deprecated public boolean isClipToScreenEnabled(); + method @Deprecated public boolean isLayoutInScreenEnabled(); + method @Deprecated public void setClipToScreenEnabled(boolean); + method @Deprecated public void setLayoutInScreenEnabled(boolean); + } + @android.widget.RemoteViews.RemoteView public class TextView extends android.view.View implements android.view.ViewTreeObserver.OnPreDrawListener { method public static int getTextColor(android.content.Context, android.content.res.TypedArray, int); method public static android.content.res.ColorStateList getTextColors(android.content.Context, android.content.res.TypedArray); diff --git a/api/system-current.txt b/api/system-current.txt index 7c66af559673..97488c1d569b 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -218,7 +218,7 @@ package android { } public static final class R.attr { - field public static final int allowClearUserDataOnFailedRestore = 16844198; // 0x10105a6 + field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600 field public static final int isVrOnly = 16844152; // 0x1010578 field public static final int requiredSystemPropertyName = 16844133; // 0x1010565 field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566 @@ -317,7 +317,6 @@ package android.app { public class AppOpsManager { method @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>); method public static String[] getOpStrs(); - method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable int[]); method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...); method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[]); method public static int opToDefaultMode(@NonNull String); @@ -936,8 +935,6 @@ package android.app.backup { method public int restorePackage(String, android.app.backup.RestoreObserver); method public int restorePackages(long, @Nullable android.app.backup.RestoreObserver, @NonNull java.util.Set<java.lang.String>, @Nullable android.app.backup.BackupManagerMonitor); method public int restorePackages(long, @Nullable android.app.backup.RestoreObserver, @NonNull java.util.Set<java.lang.String>); - method @Deprecated public int restoreSome(long, android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor, String[]); - method @Deprecated public int restoreSome(long, android.app.backup.RestoreObserver, String[]); } public class RestoreSet implements android.os.Parcelable { @@ -1092,14 +1089,11 @@ package android.app.prediction { } public static final class AppTarget.Builder { - ctor @Deprecated public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId); ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId, @NonNull String, @NonNull android.os.UserHandle); ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId, @NonNull android.content.pm.ShortcutInfo); method @NonNull public android.app.prediction.AppTarget build(); method @NonNull public android.app.prediction.AppTarget.Builder setClassName(@NonNull String); method @NonNull public android.app.prediction.AppTarget.Builder setRank(@IntRange(from=0) int); - method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle); - method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo); } public final class AppTargetEvent implements android.os.Parcelable { @@ -1602,9 +1596,6 @@ package android.content.pm { method public static void forceSafeLabels(); method @Deprecated @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager); method @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager, @FloatRange(from=0) float, int); - field @Deprecated public static final int SAFE_LABEL_FLAG_FIRST_LINE = 4; // 0x4 - field @Deprecated public static final int SAFE_LABEL_FLAG_SINGLE_LINE = 2; // 0x2 - field @Deprecated public static final int SAFE_LABEL_FLAG_TRIM = 1; // 0x1 } public abstract class PackageManager { @@ -7102,7 +7093,6 @@ package android.telecom { method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall(); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging(); - method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.WRITE_SECURE_SETTINGS}) public boolean setDefaultDialer(@Nullable String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle); field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT"; field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT"; diff --git a/api/system-removed.txt b/api/system-removed.txt index 8f7112266457..5802f6cc09b6 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -9,6 +9,10 @@ package android { package android.app { + public class AppOpsManager { + method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable int[]); + } + public class Notification implements android.os.Parcelable { method public static Class<? extends android.app.Notification.Style> getNotificationStyleClass(String); } @@ -28,6 +32,25 @@ package android.app.admin { } +package android.app.backup { + + public class RestoreSession { + method @Deprecated public int restoreSome(long, android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor, String[]); + method @Deprecated public int restoreSome(long, android.app.backup.RestoreObserver, String[]); + } + +} + +package android.app.prediction { + + public static final class AppTarget.Builder { + ctor @Deprecated public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId); + method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle); + method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo); + } + +} + package android.content { public class Intent implements java.lang.Cloneable android.os.Parcelable { @@ -60,6 +83,16 @@ package android.content { } +package android.content.pm { + + public class PackageItemInfo { + field @Deprecated public static final int SAFE_LABEL_FLAG_FIRST_LINE = 4; // 0x4 + field @Deprecated public static final int SAFE_LABEL_FLAG_SINGLE_LINE = 2; // 0x2 + field @Deprecated public static final int SAFE_LABEL_FLAG_TRIM = 1; // 0x1 + } + +} + package android.hardware.hdmi { public final class HdmiControlManager { @@ -158,6 +191,14 @@ package android.service.notification { } +package android.telecom { + + public class TelecomManager { + method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.WRITE_SECURE_SETTINGS}) public boolean setDefaultDialer(@Nullable String); + } + +} + package android.telephony { public class TelephonyManager { diff --git a/api/test-current.txt b/api/test-current.txt index 181932cf1260..0de0d7503391 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -520,8 +520,6 @@ package android.app.prediction { method @NonNull public android.app.prediction.AppTarget build(); method @NonNull public android.app.prediction.AppTarget.Builder setClassName(@NonNull String); method @NonNull public android.app.prediction.AppTarget.Builder setRank(@IntRange(from=0) int); - method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle); - method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo); } public final class AppTargetEvent implements android.os.Parcelable { diff --git a/api/test-removed.txt b/api/test-removed.txt index 83a5708a2eb3..ef0aac7eac9a 100644 --- a/api/test-removed.txt +++ b/api/test-removed.txt @@ -1,4 +1,13 @@ // Signature format: 2.0 +package android.app.prediction { + + public static final class AppTarget.Builder { + method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle); + method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo); + } + +} + package android.provider { public final class DeviceConfig { diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index ec02b121d0dd..4e0a8ebdf380 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -611,11 +611,8 @@ void StatsLogProcessor::WriteDataToDiskLocked(const ConfigKey& key, void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) { std::lock_guard<std::mutex> lock(mMetricsMutex); - const int64_t timeNs = getElapsedRealtimeNs(); // Do not write to disk if we already have in the last few seconds. - // This is to avoid overwriting files that would have the same name if we - // write twice in the same second. if (static_cast<unsigned long long> (timeNs) < mLastActiveMetricsWriteNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) { ALOGI("Statsd skipping writing active metrics to disk. Already wrote data in last %d seconds", @@ -625,13 +622,7 @@ void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) { mLastActiveMetricsWriteNs = timeNs; ProtoOutputStream proto; - for (const auto& pair : mMetricsManagers) { - const sp<MetricsManager>& metricsManager = pair.second; - uint64_t configToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG); - metricsManager->writeActiveConfigToProtoOutputStream(currentTimeNs, &proto); - proto.end(configToken); - } + WriteActiveConfigsToProtoOutputStreamLocked(currentTimeNs, DEVICE_SHUTDOWN, &proto); string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR); StorageManager::deleteFile(file_name.c_str()); @@ -644,9 +635,24 @@ void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) { proto.flush(fd.get()); } -void StatsLogProcessor::LoadActiveConfigsFromDisk() { +void StatsLogProcessor::WriteActiveConfigsToProtoOutputStream( + int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { std::lock_guard<std::mutex> lock(mMetricsMutex); + WriteActiveConfigsToProtoOutputStreamLocked(currentTimeNs, reason, proto); +} +void StatsLogProcessor::WriteActiveConfigsToProtoOutputStreamLocked( + int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { + for (const auto& pair : mMetricsManagers) { + const sp<MetricsManager>& metricsManager = pair.second; + uint64_t configToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG); + metricsManager->writeActiveConfigToProtoOutputStream(currentTimeNs, reason, proto); + proto->end(configToken); + } +} +void StatsLogProcessor::LoadActiveConfigsFromDisk() { + std::lock_guard<std::mutex> lock(mMetricsMutex); string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR); int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); if (-1 == fd) { @@ -670,6 +676,19 @@ void StatsLogProcessor::LoadActiveConfigsFromDisk() { StorageManager::deleteFile(file_name.c_str()); return; } + // Passing in mTimeBaseNs only works as long as we only load from disk is when statsd starts. + SetConfigsActiveStateLocked(activeConfigList, mTimeBaseNs); + StorageManager::deleteFile(file_name.c_str()); +} + +void StatsLogProcessor::SetConfigsActiveState(const ActiveConfigList& activeConfigList, + int64_t currentTimeNs) { + std::lock_guard<std::mutex> lock(mMetricsMutex); + SetConfigsActiveStateLocked(activeConfigList, currentTimeNs); +} + +void StatsLogProcessor::SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList, + int64_t currentTimeNs) { for (int i = 0; i < activeConfigList.config_size(); i++) { const auto& config = activeConfigList.config(i); ConfigKey key(config.uid(), config.id()); @@ -679,11 +698,9 @@ void StatsLogProcessor::LoadActiveConfigsFromDisk() { continue; } VLOG("Setting active config %s", key.ToString().c_str()); - it->second->loadActiveConfig(config, mTimeBaseNs); + it->second->loadActiveConfig(config, currentTimeNs); } VLOG("Successfully loaded %d active configs.", activeConfigList.config_size()); - - StorageManager::deleteFile(file_name.c_str()); } void StatsLogProcessor::WriteDataToDiskLocked(const DumpReportReason dumpReportReason, diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 0dc597b4cb02..92aa425a731d 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -31,17 +31,6 @@ namespace android { namespace os { namespace statsd { -// Keep this in sync with DumpReportReason enum in stats_log.proto -enum DumpReportReason { - DEVICE_SHUTDOWN = 1, - CONFIG_UPDATED = 2, - CONFIG_REMOVED = 3, - GET_DATA_CALLED = 4, - ADB_DUMP = 5, - CONFIG_RESET = 6, - STATSCOMPANION_DIED = 7, - TERMINATION_SIGNAL_RECEIVED = 8 -}; class StatsLogProcessor : public ConfigListener { public: @@ -92,9 +81,16 @@ public: /* Persist configs containing metrics with active activations to disk. */ void SaveActiveConfigsToDisk(int64_t currentTimeNs); + /* Writes the current active status/ttl for all configs and metrics to ProtoOutputStream. */ + void WriteActiveConfigsToProtoOutputStream( + int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); + /* Load configs containing metrics with active activations from disk. */ void LoadActiveConfigsFromDisk(); + /* Sets the active status/ttl for all configs and metrics to the status in ActiveConfigList. */ + void SetConfigsActiveState(const ActiveConfigList& activeConfigList, int64_t currentTimeNs); + // Reset all configs. void resetConfigs(); @@ -158,6 +154,12 @@ private: void GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs); + void WriteActiveConfigsToProtoOutputStreamLocked( + int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); + + void SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList, + int64_t currentTimeNs); + void WriteDataToDiskLocked(const DumpReportReason dumpReportReason, const DumpLatency dumpLatency); void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs, @@ -224,6 +226,7 @@ private: FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations); FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes); + FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 623a1f2cdafb..8191d37bb603 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -1478,8 +1478,21 @@ void StatsService::binderDied(const wp <IBinder>& who) { StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec()); if (mProcessor != nullptr) { ALOGW("Reset statsd upon system server restarts."); + int64_t systemServerRestartNs = getElapsedRealtimeNs(); + ProtoOutputStream proto; + mProcessor->WriteActiveConfigsToProtoOutputStream(systemServerRestartNs, + STATSCOMPANION_DIED, &proto); + mProcessor->WriteDataToDisk(STATSCOMPANION_DIED, FAST); mProcessor->resetConfigs(); + + std::string serializedActiveConfigs; + if (proto.serializeToString(&serializedActiveConfigs)) { + ActiveConfigList activeConfigs; + if (activeConfigs.ParseFromString(serializedActiveConfigs)) { + mProcessor->SetConfigsActiveState(activeConfigs, systemServerRestartNs); + } + } } mAnomalyAlarmMonitor->setStatsCompanionService(nullptr); mPeriodicAlarmMonitor->setStatsCompanionService(nullptr); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 936f7db52c38..a4e6d7fec4cc 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -427,6 +427,7 @@ private: std::shared_ptr<LogEventQueue> mEventQueue; + FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); FRIEND_TEST(StatsServiceTest, TestAddConfig_simple); FRIEND_TEST(StatsServiceTest, TestAddConfig_empty); FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid); diff --git a/cmds/statsd/src/active_config_list.proto b/cmds/statsd/src/active_config_list.proto index ef8e50bb2596..992983358ae6 100644 --- a/cmds/statsd/src/active_config_list.proto +++ b/cmds/statsd/src/active_config_list.proto @@ -26,7 +26,18 @@ message ActiveEventActivation { // Time left in activation. When this proto is loaded after device boot, // the activation should be set to active for this duration. + // This field will only be set when the state is ACTIVE optional int64 remaining_ttl_nanos = 2; + + enum State { + UNNKNOWN = 0; + // This metric should activate for remaining_ttl_nanos when we load the activations. + ACTIVE = 1; + // When we load the activations, this metric should activate on next boot for the tll + // specified in the config. + ACTIVATE_ON_BOOT = 2; + } + optional State state = 3; } message ActiveMetric { diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index f53ac1b77021..abcccf89c2a8 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -364,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. @@ -6357,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/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 9ad7f09ab512..d913427a05dc 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -19,6 +19,7 @@ #include "MetricProducer.h" using android::util::FIELD_COUNT_REPEATED; +using android::util::FIELD_TYPE_ENUM; using android::util::FIELD_TYPE_INT32; using android::util::FIELD_TYPE_INT64; using android::util::FIELD_TYPE_MESSAGE; @@ -37,6 +38,7 @@ const int FIELD_ID_ACTIVE_METRIC_ACTIVATION = 2; // for ActiveEventActivation const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1; const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2; +const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3; void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) { if (!mIsActive) { @@ -165,17 +167,21 @@ void MetricProducer::loadActiveMetricLocked(const ActiveMetric& activeMetric, continue; } auto& activation = it->second; - // We don't want to change the ttl for future activations, so we set the start_ns - // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos - activation->start_ns = - currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns; - activation->state = ActivationState::kActive; - mIsActive = true; + if (activeEventActivation.state() == ActiveEventActivation::ACTIVE) { + // We don't want to change the ttl for future activations, so we set the start_ns + // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos + activation->start_ns = + currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns; + activation->state = ActivationState::kActive; + mIsActive = true; + } else if (activeEventActivation.state() == ActiveEventActivation::ACTIVATE_ON_BOOT) { + activation->state = ActivationState::kActiveOnBoot; + } } } void MetricProducer::writeActiveMetricToProtoOutputStream( - int64_t currentTimeNs, ProtoOutputStream* proto) { + int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_METRIC_ID, (long long)mMetricId); for (auto& it : mEventActivationMap) { const int atom_matcher_index = it.first; @@ -196,9 +202,22 @@ void MetricProducer::writeActiveMetricToProtoOutputStream( activation->start_ns + activation->ttl_ns - currentTimeNs; proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS, (long long)remainingTtlNs); + proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE, + ActiveEventActivation::ACTIVE); + } else if (ActivationState::kActiveOnBoot == activation->state) { - proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS, - (long long)activation->ttl_ns); + if (reason == DEVICE_SHUTDOWN || reason == TERMINATION_SIGNAL_RECEIVED) { + proto->write( + FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS, + (long long)activation->ttl_ns); + proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE, + ActiveEventActivation::ACTIVE); + } else if (reason == STATSCOMPANION_DIED) { + // We are saving because of system server death, not due to a device shutdown. + // Next time we load, we do not want to activate metrics that activate on boot. + proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE, + ActiveEventActivation::ACTIVATE_ON_BOOT); + } } proto->end(activationToken); } diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index ec3484c80209..3ddbef44718d 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -35,6 +35,18 @@ namespace android { namespace os { namespace statsd { +// Keep this in sync with DumpReportReason enum in stats_log.proto +enum DumpReportReason { + DEVICE_SHUTDOWN = 1, + CONFIG_UPDATED = 2, + CONFIG_REMOVED = 3, + GET_DATA_CALLED = 4, + ADB_DUMP = 5, + CONFIG_RESET = 6, + STATSCOMPANION_DIED = 7, + TERMINATION_SIGNAL_RECEIVED = 8 +}; + // If the metric has no activation requirement, it will be active once the metric producer is // created. // If the metric needs to be activated by atoms, the metric producer will start @@ -244,7 +256,7 @@ public: void flushIfExpire(int64_t elapsedTimestampNs); void writeActiveMetricToProtoOutputStream( - int64_t currentTimeNs, ProtoOutputStream* proto); + int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); protected: virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0; virtual void onSlicedConditionMayChangeLocked(bool overallCondition, @@ -268,8 +280,6 @@ protected: return mIsActive; } - void prepActiveForBootIfNecessaryLocked(int64_t currentTimeNs); - void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs); virtual void prepareFirstBucketLocked() {}; @@ -412,6 +422,7 @@ protected: FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations); FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes); + FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 947f37782fcc..207a7dd87215 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -528,14 +528,14 @@ void MetricsManager::loadActiveConfig(const ActiveConfig& config, int64_t curren } void MetricsManager::writeActiveConfigToProtoOutputStream( - int64_t currentTimeNs, ProtoOutputStream* proto) { + int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_CONFIG_ID, (long long)mConfigKey.GetId()); proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_CONFIG_UID, mConfigKey.GetUid()); for (int metricIndex : mMetricIndexesWithActivation) { const auto& metric = mAllMetricProducers[metricIndex]; const uint64_t metricToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_ACTIVE_CONFIG_METRIC); - metric->writeActiveMetricToProtoOutputStream(currentTimeNs, proto); + metric->writeActiveMetricToProtoOutputStream(currentTimeNs, reason, proto); proto->end(metricToken); } } diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 818131efe138..da3be061cb57 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -141,7 +141,7 @@ public: void loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs); void writeActiveConfigToProtoOutputStream( - int64_t currentTimeNs, ProtoOutputStream* proto); + int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); private: // For test only. @@ -290,6 +290,7 @@ private: FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations); FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes); + FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); }; } // namespace statsd diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index 49b4e904d93c..fe25a257aa67 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -13,12 +13,14 @@ // limitations under the License. #include "StatsLogProcessor.h" +#include "StatsService.h" #include "config/ConfigKey.h" #include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "guardrail/StatsdStats.h" #include "logd/LogEvent.h" #include "packages/UidMap.h" +#include "storage/StorageManager.h" #include "statslog.h" #include <gmock/gmock.h> @@ -97,7 +99,8 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { ConfigKey key(100, 12345); EXPECT_CALL(mockMetricsManager, byteSize()) .Times(1) - .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * .95))); + .WillRepeatedly(::testing::Return(int( + StatsdStats::kMaxMetricsBytesPerConfig * .95))); // Expect only one broadcast despite always returning a size that should trigger broadcast. p.flushIfNecessaryLocked(1, key, mockMetricsManager); @@ -128,7 +131,7 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { ConfigKey key(100, 12345); EXPECT_CALL(mockMetricsManager, byteSize()) .Times(1) - .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2))); + .WillRepeatedly(::testing::Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2))); EXPECT_CALL(mockMetricsManager, dropData(_)).Times(1); @@ -1482,6 +1485,236 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi // }}}--------------------------------------------------------------------------- } +TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) { + int uid = 9876; + long configId = 12341; + + // Create config with 3 metrics: + // Metric 1: Activate on 2 activations, 1 on boot, 1 immediate. + // Metric 2: Activate on 2 activations, 1 on boot, 1 immediate. + // Metric 3: Always active + StatsdConfig config1; + config1.set_id(configId); + config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + auto jobStartMatcher = CreateStartScheduledJobAtomMatcher(); + auto jobFinishMatcher = CreateFinishScheduledJobAtomMatcher(); + *config1.add_atom_matcher() = wakelockAcquireMatcher; + *config1.add_atom_matcher() = screenOnMatcher; + *config1.add_atom_matcher() = jobStartMatcher; + *config1.add_atom_matcher() = jobFinishMatcher; + + long metricId1 = 1234561; + long metricId2 = 1234562; + long metricId3 = 1234563; + + auto countMetric1 = config1.add_count_metric(); + countMetric1->set_id(metricId1); + countMetric1->set_what(wakelockAcquireMatcher.id()); + countMetric1->set_bucket(FIVE_MINUTES); + + auto countMetric2 = config1.add_count_metric(); + countMetric2->set_id(metricId2); + countMetric2->set_what(wakelockAcquireMatcher.id()); + countMetric2->set_bucket(FIVE_MINUTES); + + auto countMetric3 = config1.add_count_metric(); + countMetric3->set_id(metricId3); + countMetric3->set_what(wakelockAcquireMatcher.id()); + countMetric3->set_bucket(FIVE_MINUTES); + + // Metric 1 activates on boot for wakelock acquire, immediately for screen on. + auto metric1Activation = config1.add_metric_activation(); + metric1Activation->set_metric_id(metricId1); + auto metric1ActivationTrigger1 = metric1Activation->add_event_activation(); + metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id()); + metric1ActivationTrigger1->set_ttl_seconds(100); + metric1ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT); + auto metric1ActivationTrigger2 = metric1Activation->add_event_activation(); + metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id()); + metric1ActivationTrigger2->set_ttl_seconds(200); + metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY); + + // Metric 2 activates on boot for scheduled job start, immediately for scheduled job finish. + auto metric2Activation = config1.add_metric_activation(); + metric2Activation->set_metric_id(metricId2); + auto metric2ActivationTrigger1 = metric2Activation->add_event_activation(); + metric2ActivationTrigger1->set_atom_matcher_id(jobStartMatcher.id()); + metric2ActivationTrigger1->set_ttl_seconds(100); + metric2ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT); + auto metric2ActivationTrigger2 = metric2Activation->add_event_activation(); + metric2ActivationTrigger2->set_atom_matcher_id(jobFinishMatcher.id()); + metric2ActivationTrigger2->set_ttl_seconds(200); + metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY); + + // Send the config. + StatsService service(nullptr, nullptr); + string serialized = config1.SerializeAsString(); + service.addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()}); + + // Make sure the config is stored on disk. Otherwise, we will not reset on system server death. + StatsdConfig tmpConfig; + ConfigKey cfgKey1(uid, configId); + EXPECT_TRUE(StorageManager::readConfigFromDisk(cfgKey1, &tmpConfig)); + + // Metric 1 is not active. + // Metric 2 is not active. + // Metric 3 is active. + // {{{--------------------------------------------------------------------------- + sp<StatsLogProcessor> processor = service.mProcessor; + EXPECT_EQ(1, processor->mMetricsManagers.size()); + auto it = processor->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor->mMetricsManagers.end()); + auto& metricsManager1 = it->second; + EXPECT_TRUE(metricsManager1->isActive()); + EXPECT_EQ(3, metricsManager1->mAllMetricProducers.size()); + + auto& metricProducer1 = metricsManager1->mAllMetricProducers[0]; + EXPECT_EQ(metricId1, metricProducer1->getMetricId()); + EXPECT_FALSE(metricProducer1->isActive()); + + auto& metricProducer2 = metricsManager1->mAllMetricProducers[1]; + EXPECT_EQ(metricId2, metricProducer2->getMetricId()); + EXPECT_FALSE(metricProducer2->isActive()); + + auto& metricProducer3 = metricsManager1->mAllMetricProducers[2]; + EXPECT_EQ(metricId3, metricProducer3->getMetricId()); + EXPECT_TRUE(metricProducer3->isActive()); + + // Check event activations. + EXPECT_EQ(metricsManager1->mAllAtomMatchers.size(), 4); + EXPECT_EQ(metricsManager1->mAllAtomMatchers[0]->getId(), + metric1ActivationTrigger1->atom_matcher_id()); + const auto& activation1 = metricProducer1->mEventActivationMap.at(0); + EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); + EXPECT_EQ(0, activation1->start_ns); + EXPECT_EQ(kNotActive, activation1->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType); + + EXPECT_EQ(metricsManager1->mAllAtomMatchers[1]->getId(), + metric1ActivationTrigger2->atom_matcher_id()); + const auto& activation2 = metricProducer1->mEventActivationMap.at(1); + EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns); + EXPECT_EQ(0, activation2->start_ns); + EXPECT_EQ(kNotActive, activation2->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType); + + EXPECT_EQ(metricsManager1->mAllAtomMatchers[2]->getId(), + metric2ActivationTrigger1->atom_matcher_id()); + const auto& activation3 = metricProducer2->mEventActivationMap.at(2); + EXPECT_EQ(100 * NS_PER_SEC, activation3->ttl_ns); + EXPECT_EQ(0, activation3->start_ns); + EXPECT_EQ(kNotActive, activation3->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activation3->activationType); + + EXPECT_EQ(metricsManager1->mAllAtomMatchers[3]->getId(), + metric2ActivationTrigger2->atom_matcher_id()); + const auto& activation4 = metricProducer2->mEventActivationMap.at(3); + EXPECT_EQ(200 * NS_PER_SEC, activation4->ttl_ns); + EXPECT_EQ(0, activation4->start_ns); + EXPECT_EQ(kNotActive, activation4->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation4->activationType); + // }}}------------------------------------------------------------------------------ + + // Trigger Activation 1 for Metric 1. Should activate on boot. + // Trigger Activation 4 for Metric 2. Should activate immediately. + long configAddedTimeNs = metricsManager1->mLastReportTimeNs; + std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; + auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 1 + configAddedTimeNs); + processor->OnLogEvent(event.get()); + + event = CreateFinishScheduledJobEvent(attributions1, "finish1", 2 + configAddedTimeNs); + processor->OnLogEvent(event.get()); + + // Metric 1 is not active; Activation 1 set to kActiveOnBoot + // Metric 2 is active. Activation 4 set to kActive + // Metric 3 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_FALSE(metricProducer1->isActive()); + EXPECT_EQ(0, activation1->start_ns); + EXPECT_EQ(kActiveOnBoot, activation1->state); + EXPECT_EQ(0, activation2->start_ns); + EXPECT_EQ(kNotActive, activation2->state); + + EXPECT_TRUE(metricProducer2->isActive()); + EXPECT_EQ(0, activation3->start_ns); + EXPECT_EQ(kNotActive, activation3->state); + EXPECT_EQ(2 + configAddedTimeNs, activation4->start_ns); + EXPECT_EQ(kActive, activation4->state); + + EXPECT_TRUE(metricProducer3->isActive()); + // }}}----------------------------------------------------------------------------- + + // Can't fake time with StatsService. + // Lets get a time close to the system server death time and make sure it's sane. + int64_t approximateSystemServerDeath = getElapsedRealtimeNs(); + EXPECT_TRUE(approximateSystemServerDeath > 2 + configAddedTimeNs); + EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs); + + // System server dies. + service.binderDied(nullptr); + + // We should have a new metrics manager. Lets get it and ensure activation status is restored. + // {{{--------------------------------------------------------------------------- + EXPECT_EQ(1, processor->mMetricsManagers.size()); + it = processor->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor->mMetricsManagers.end()); + auto& metricsManager2 = it->second; + EXPECT_TRUE(metricsManager2->isActive()); + EXPECT_EQ(3, metricsManager2->mAllMetricProducers.size()); + + auto& metricProducer1001 = metricsManager2->mAllMetricProducers[0]; + EXPECT_EQ(metricId1, metricProducer1001->getMetricId()); + EXPECT_FALSE(metricProducer1001->isActive()); + + auto& metricProducer1002 = metricsManager2->mAllMetricProducers[1]; + EXPECT_EQ(metricId2, metricProducer1002->getMetricId()); + EXPECT_TRUE(metricProducer1002->isActive()); + + auto& metricProducer1003 = metricsManager2->mAllMetricProducers[2]; + EXPECT_EQ(metricId3, metricProducer1003->getMetricId()); + EXPECT_TRUE(metricProducer1003->isActive()); + + // Check event activations. + // Activation 1 is kActiveOnBoot. + // Activation 2 and 3 are not active. + // Activation 4 is active. + EXPECT_EQ(metricsManager2->mAllAtomMatchers.size(), 4); + EXPECT_EQ(metricsManager2->mAllAtomMatchers[0]->getId(), + metric1ActivationTrigger1->atom_matcher_id()); + const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0); + EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns); + EXPECT_EQ(0, activation1001->start_ns); + EXPECT_EQ(kActiveOnBoot, activation1001->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001->activationType); + + EXPECT_EQ(metricsManager2->mAllAtomMatchers[1]->getId(), + metric1ActivationTrigger2->atom_matcher_id()); + const auto& activation1002 = metricProducer1001->mEventActivationMap.at(1); + EXPECT_EQ(200 * NS_PER_SEC, activation1002->ttl_ns); + EXPECT_EQ(0, activation1002->start_ns); + EXPECT_EQ(kNotActive, activation1002->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1002->activationType); + + EXPECT_EQ(metricsManager2->mAllAtomMatchers[2]->getId(), + metric2ActivationTrigger1->atom_matcher_id()); + const auto& activation1003 = metricProducer1002->mEventActivationMap.at(2); + EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns); + EXPECT_EQ(0, activation1003->start_ns); + EXPECT_EQ(kNotActive, activation1003->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activation1003->activationType); + + EXPECT_EQ(metricsManager2->mAllAtomMatchers[3]->getId(), + metric2ActivationTrigger2->atom_matcher_id()); + const auto& activation1004 = metricProducer1002->mEventActivationMap.at(3); + EXPECT_EQ(200 * NS_PER_SEC, activation1004->ttl_ns); + EXPECT_EQ(2 + configAddedTimeNs, activation1004->start_ns); + EXPECT_EQ(kActive, activation1004->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1004->activationType); + // }}}------------------------------------------------------------------------------ +} + #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index f8d7b516ff07..45c716809d8f 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -4370,6 +4370,7 @@ public class AppOpsManager { * {@link #getOpsForPackage(int, String, String...)})}. * * @hide + * @removed */ @Deprecated @SystemApi 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/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/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java index 19f4335893cb..972762152d3a 100644 --- a/core/java/android/app/admin/SecurityLog.java +++ b/core/java/android/app/admin/SecurityLog.java @@ -636,6 +636,11 @@ public class SecurityLog { public int hashCode() { return Objects.hash(mEvent, mId); } + + /** @hide */ + public boolean eventEquals(SecurityEvent other) { + return other != null && mEvent.equals(other.mEvent); + } } /** * Retrieve all security logs and return immediately. diff --git a/core/java/android/app/backup/RestoreSession.java b/core/java/android/app/backup/RestoreSession.java index 084b13b61617..933670415f2e 100644 --- a/core/java/android/app/backup/RestoreSession.java +++ b/core/java/android/app/backup/RestoreSession.java @@ -214,6 +214,7 @@ public class RestoreSession { * * @deprecated use {@link RestoreSession#restorePackages(long, RestoreObserver, * BackupManagerMonitor, Set)} instead. + * @removed */ @Deprecated public int restoreSome(long token, RestoreObserver observer, BackupManagerMonitor monitor, @@ -240,6 +241,7 @@ public class RestoreSession { * * @deprecated use {@link RestoreSession#restorePackages(long, RestoreObserver, Set)} * instead. + * @removed */ @Deprecated public int restoreSome(long token, RestoreObserver observer, String[] packages) { 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/prediction/AppTarget.java b/core/java/android/app/prediction/AppTarget.java index 61e4569c1228..147c5000e333 100644 --- a/core/java/android/app/prediction/AppTarget.java +++ b/core/java/android/app/prediction/AppTarget.java @@ -206,6 +206,7 @@ public final class AppTarget implements Parcelable { /** * @deprecated Use the other Builder constructors. * @hide + * @removed */ @Deprecated @SystemApi @@ -244,6 +245,7 @@ public final class AppTarget implements Parcelable { /** * @deprecated Use the appropriate constructor. + * @removed */ @NonNull @Deprecated @@ -258,6 +260,7 @@ public final class AppTarget implements Parcelable { /** * @deprecated Use the appropriate constructor. + * @removed */ @NonNull @Deprecated 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/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 7fa436084246..749a011153cd 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -281,20 +281,13 @@ public final class UsageStatsManager { /** * Gets application usage stats for the given time range, aggregated by the specified interval. - * <p>The returned list will contain a {@link UsageStats} object for each package that - * has data for an interval that is a subset of the time range given. To illustrate:</p> - * <pre> - * intervalType = INTERVAL_YEARLY - * beginTime = 2013 - * endTime = 2015 (exclusive) * - * Results: - * 2013 - com.example.alpha - * 2013 - com.example.beta - * 2014 - com.example.alpha - * 2014 - com.example.beta - * 2014 - com.example.charlie - * </pre> + * <p> + * The returned list will contain one or more {@link UsageStats} objects for each package, with + * usage data that covers at least the given time range. + * Note: The begin and end times of the time range may be expanded to the nearest whole interval + * period. + * </p> * * <p> The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS} </p> * diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 71242fbac9a5..7cdd2683905f 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -820,6 +820,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall private String setCallingPackage(String callingPackage) { final String original = mCallingPackage.get(); mCallingPackage.set(callingPackage); + onCallingPackageChanged(); return original; } @@ -845,6 +846,15 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall return pkg; } + /** {@hide} */ + public final @Nullable String getCallingPackageUnchecked() { + return mCallingPackage.get(); + } + + /** {@hide} */ + public void onCallingPackageChanged() { + } + /** * Opaque token representing the identity of an incoming IPC. */ 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/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java index ff7b34773268..081c5ad78762 100644 --- a/core/java/android/content/pm/PackageItemInfo.java +++ b/core/java/android/content/pm/PackageItemInfo.java @@ -62,6 +62,7 @@ public class PackageItemInfo { * * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_TRIM} instead * @hide + * @removed */ @Deprecated @SystemApi @@ -75,6 +76,7 @@ public class PackageItemInfo { * * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_SINGLE_LINE} instead * @hide + * @removed */ @Deprecated @SystemApi @@ -88,6 +90,7 @@ public class PackageItemInfo { * * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_FIRST_LINE} instead * @hide + * @removed */ @Deprecated @SystemApi diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 3a4741a97212..1c9740728f1f 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -976,8 +976,7 @@ public class Build { * to come. Con permiso, Capitan. The hall is rented, the orchestra * engaged. It's now time to see if you can dance.</em> */ - public static final int Q = CUR_DEVELOPMENT; - + public static final int Q = 29; } /** The type of build, like "user" or "eng". */ 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/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 4ac485099da8..22ce39d1784a 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -917,8 +917,17 @@ public final class DocumentsContract { * @see #getDocumentId(Uri) */ public static Uri buildDocumentUri(String authority, String documentId) { + return getBaseDocumentUriBuilder(authority).appendPath(documentId).build(); + } + + /** {@hide} */ + public static Uri buildBaseDocumentUri(String authority) { + return getBaseDocumentUriBuilder(authority).build(); + } + + private static Uri.Builder getBaseDocumentUriBuilder(String authority) { return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) - .authority(authority).appendPath(PATH_DOCUMENT).appendPath(documentId).build(); + .authority(authority).appendPath(PATH_DOCUMENT); } /** 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/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/SurfaceView.java b/core/java/android/view/SurfaceView.java index 1812d291dd8f..7fdda2a22c17 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -33,7 +33,6 @@ import android.graphics.Region; import android.graphics.RenderNode; import android.os.Build; import android.os.Handler; -import android.os.IBinder; import android.os.Looper; import android.os.SystemClock; import android.util.AttributeSet; @@ -120,10 +119,11 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb final Rect mScreenRect = new Rect(); SurfaceSession mSurfaceSession; - SurfaceControlWithBackground mSurfaceControl; + SurfaceControl mSurfaceControl; // In the case of format changes we switch out the surface in-place // we need to preserve the old one until the new one has drawn. - SurfaceControlWithBackground mDeferredDestroySurfaceControl; + SurfaceControl mDeferredDestroySurfaceControl; + SurfaceControl mBackgroundControl; final Rect mTmpRect = new Rect(); final Configuration mConfiguration = new Configuration(); @@ -487,6 +487,29 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb } } + private void updateBackgroundVisibilityInTransaction(SurfaceControl viewRoot) { + if (mBackgroundControl == null) { + return; + } + if ((mSubLayer < 0) && ((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)) { + mBackgroundControl.show(); + mBackgroundControl.setRelativeLayer(viewRoot, Integer.MIN_VALUE); + } else { + mBackgroundControl.hide(); + } + } + + private void releaseSurfaces() { + if (mSurfaceControl != null) { + mSurfaceControl.remove(); + mSurfaceControl = null; + } + if (mBackgroundControl != null) { + mBackgroundControl.remove(); + mBackgroundControl = null; + } + } + /** @hide */ protected void updateSurface() { if (!mHaveFrame) { @@ -553,14 +576,21 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb updateOpaqueFlag(); final String name = "SurfaceView - " + viewRoot.getTitle().toString(); - mSurfaceControl = new SurfaceControlWithBackground( - name, - (mSurfaceFlags & SurfaceControl.OPAQUE) != 0, - new SurfaceControl.Builder(mSurfaceSession) - .setBufferSize(mSurfaceWidth, mSurfaceHeight) - .setFormat(mFormat) - .setParent(viewRoot.getSurfaceControl()) - .setFlags(mSurfaceFlags)); + mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession) + .setName(name) + .setOpaque((mSurfaceFlags & SurfaceControl.OPAQUE) != 0) + .setBufferSize(mSurfaceWidth, mSurfaceHeight) + .setFormat(mFormat) + .setParent(viewRoot.getSurfaceControl()) + .setFlags(mSurfaceFlags) + .build(); + mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession) + .setName("Background for -" + name) + .setOpaque(true) + .setColorLayer() + .setParent(mSurfaceControl) + .build(); + } else if (mSurfaceControl == null) { return; } @@ -577,11 +607,13 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb SurfaceControl.openTransaction(); try { mSurfaceControl.setLayer(mSubLayer); + if (mViewVisibility) { mSurfaceControl.show(); } else { mSurfaceControl.hide(); } + updateBackgroundVisibilityInTransaction(viewRoot.getSurfaceControl()); // While creating the surface, we will set it's initial // geometry. Outside of that though, we should generally @@ -667,7 +699,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb } if (creating) { - mSurface.copyFrom(mSurfaceControl.mForegroundControl); + mSurface.copyFrom(mSurfaceControl); } if (sizeChanged && getContext().getApplicationInfo().targetSdkVersion @@ -677,7 +709,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb // existing {@link Surface} will be ignored when the size changes. // Therefore, we must explicitly recreate the {@link Surface} in these // cases. - mSurface.createFrom(mSurfaceControl.mForegroundControl); + mSurface.createFrom(mSurfaceControl); } if (visible && mSurface.isValid()) { @@ -724,8 +756,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb if (mSurfaceControl != null && !mSurfaceCreated) { mSurface.release(); - mSurfaceControl.remove(); - mSurfaceControl = null; + releaseSurfaces(); } } } catch (Exception ex) { @@ -826,8 +857,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb private void setParentSpaceRectangle(Rect position, long frameNumber) { final ViewRootImpl viewRoot = getViewRootImpl(); - applySurfaceTransforms(mSurfaceControl.mForegroundControl, position, frameNumber); - applySurfaceTransforms(mSurfaceControl.mBackgroundControl, position, frameNumber); + applySurfaceTransforms(mSurfaceControl, position, frameNumber); applyChildSurfaceTransaction_renderWorker(mRtTransaction, viewRoot.mSurface, frameNumber); @@ -892,13 +922,10 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb if (frameNumber > 0) { final ViewRootImpl viewRoot = getViewRootImpl(); - mRtTransaction.deferTransactionUntilSurface(mSurfaceControl.mForegroundControl, viewRoot.mSurface, - frameNumber); - mRtTransaction.deferTransactionUntilSurface(mSurfaceControl.mBackgroundControl, viewRoot.mSurface, + mRtTransaction.deferTransactionUntilSurface(mSurfaceControl, viewRoot.mSurface, frameNumber); } - mRtTransaction.hide(mSurfaceControl.mForegroundControl); - mRtTransaction.hide(mSurfaceControl.mBackgroundControl); + mRtTransaction.hide(mSurfaceControl); mRtTransaction.apply(); } }; @@ -945,7 +972,19 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb * @hide */ public void setResizeBackgroundColor(int bgColor) { - mSurfaceControl.setBackgroundColor(bgColor); + if (mBackgroundControl == null) { + return; + } + + final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f, + Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f }; + + SurfaceControl.openTransaction(); + try { + mBackgroundControl.setColor(colorComponents); + } finally { + SurfaceControl.closeTransaction(); + } } @UnsupportedAppUsage @@ -1129,134 +1168,6 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb * @return The SurfaceControl for this SurfaceView. */ public SurfaceControl getSurfaceControl() { - return mSurfaceControl.mForegroundControl; - } - - class SurfaceControlWithBackground { - SurfaceControl mForegroundControl; - SurfaceControl mBackgroundControl; - private boolean mOpaque = true; - public boolean mVisible = false; - - public SurfaceControlWithBackground(String name, boolean opaque, SurfaceControl.Builder b) - throws Exception { - mForegroundControl = b.setName(name).build(); - mBackgroundControl = b.setName("Background for -" + name) - // Unset the buffer size of the background color layer. - .setBufferSize(0, 0) - .setColorLayer() - .build(); - - mOpaque = opaque; - } - - public void setAlpha(float alpha) { - mForegroundControl.setAlpha(alpha); - mBackgroundControl.setAlpha(alpha); - } - - public void setLayer(int zorder) { - mForegroundControl.setLayer(zorder); - // -3 is below all other child layers as SurfaceView never goes below -2 - mBackgroundControl.setLayer(-3); - } - - public void setPosition(float x, float y) { - mForegroundControl.setPosition(x, y); - mBackgroundControl.setPosition(x, y); - } - - public void setBufferSize(int w, int h) { - mForegroundControl.setBufferSize(w, h); - // The background surface is a color layer so we do not set a size. - } - - public void setWindowCrop(Rect crop) { - mForegroundControl.setWindowCrop(crop); - mBackgroundControl.setWindowCrop(crop); - } - - public void setWindowCrop(int width, int height) { - mForegroundControl.setWindowCrop(width, height); - mBackgroundControl.setWindowCrop(width, height); - } - - public void setLayerStack(int layerStack) { - mForegroundControl.setLayerStack(layerStack); - mBackgroundControl.setLayerStack(layerStack); - } - - public void setOpaque(boolean isOpaque) { - mForegroundControl.setOpaque(isOpaque); - mOpaque = isOpaque; - updateBackgroundVisibility(); - } - - public void setSecure(boolean isSecure) { - mForegroundControl.setSecure(isSecure); - } - - public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { - mForegroundControl.setMatrix(dsdx, dtdx, dsdy, dtdy); - mBackgroundControl.setMatrix(dsdx, dtdx, dsdy, dtdy); - } - - public void hide() { - mForegroundControl.hide(); - mVisible = false; - updateBackgroundVisibility(); - } - - public void show() { - mForegroundControl.show(); - mVisible = true; - updateBackgroundVisibility(); - } - - public void remove() { - mForegroundControl.remove(); - mBackgroundControl.remove(); - } - - public void release() { - mForegroundControl.release(); - mBackgroundControl.release(); - } - - public void setTransparentRegionHint(Region region) { - mForegroundControl.setTransparentRegionHint(region); - mBackgroundControl.setTransparentRegionHint(region); - } - - public void deferTransactionUntil(IBinder handle, long frame) { - mForegroundControl.deferTransactionUntil(handle, frame); - mBackgroundControl.deferTransactionUntil(handle, frame); - } - - public void deferTransactionUntil(Surface barrier, long frame) { - mForegroundControl.deferTransactionUntil(barrier, frame); - mBackgroundControl.deferTransactionUntil(barrier, frame); - } - - /** Set the color to fill the background with. */ - private void setBackgroundColor(int bgColor) { - final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f, - Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f }; - - SurfaceControl.openTransaction(); - try { - mBackgroundControl.setColor(colorComponents); - } finally { - SurfaceControl.closeTransaction(); - } - } - - void updateBackgroundVisibility() { - if (mOpaque && mVisible) { - mBackgroundControl.show(); - } else { - mBackgroundControl.hide(); - } - } + return mSurfaceControl; } } 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/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 20fc0b1a9f0b..3779779e9f21 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -887,6 +887,7 @@ public class PopupWindow { * * @return true if popup will be clipped to the screen instead of the window, false otherwise * @deprecated Use {@link #isClippedToScreen()} instead + * @removed */ @Deprecated public boolean isClipToScreenEnabled() { @@ -901,6 +902,7 @@ public class PopupWindow { * the {@link #update()} methods.</p> * * @deprecated Use {@link #setIsClippedToScreen(boolean)} instead + * @removed */ @Deprecated public void setClipToScreenEnabled(boolean enabled) { @@ -988,6 +990,7 @@ public class PopupWindow { * @return true if the window will always be positioned in screen coordinates. * * @deprecated Use {@link #isLaidOutInScreen()} instead + * @removed */ @Deprecated public boolean isLayoutInScreenEnabled() { @@ -1001,6 +1004,7 @@ public class PopupWindow { * * @param enabled true if the popup should always be positioned in screen coordinates * @deprecated Use {@link #setIsLaidOutInScreen(boolean)} instead + * @removed */ @Deprecated public void setLayoutInScreenEnabled(boolean enabled) { 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/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 2736c6a7149f..dbc15b31b6c3 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -172,6 +172,11 @@ public final class Zygote { */ public static final int SOCKET_BUFFER_SIZE = 256; + /** + * @hide for internal use only + */ + private static final int PRIORITY_MAX = -20; + /** a prototype instance for a future List.toArray() */ protected static final int[][] INT_ARRAY_2D = new int[0][0]; @@ -236,8 +241,7 @@ public final class Zygote { int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir, int targetSdkVersion) { ZygoteHooks.preFork(); - // Resets nice priority for zygote process. - resetNicePriority(); + int pid = nativeForkAndSpecialize( uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet, appDataDir); @@ -249,6 +253,7 @@ public final class Zygote { // Note that this event ends at the end of handleChildProc, Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork"); } + ZygoteHooks.postForkCommon(); return pid; } @@ -335,15 +340,16 @@ public final class Zygote { public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) { ZygoteHooks.preFork(); - // Resets nice priority for zygote process. - resetNicePriority(); + int pid = nativeForkSystemServer( uid, gid, gids, runtimeFlags, rlimits, permittedCapabilities, effectiveCapabilities); + // Enable tracing as soon as we enter the system_server. if (pid == 0) { Trace.setTracingEnabled(true, runtimeFlags); } + ZygoteHooks.postForkCommon(); return pid; } @@ -461,13 +467,16 @@ public final class Zygote { /** * Fork a new unspecialized app process from the zygote * + * @param usapPoolSocket The server socket the USAP will call accept on * @param sessionSocketRawFDs Anonymous session sockets that are currently open + * @param isPriorityFork Value controlling the process priority level until accept is called * @return In the Zygote process this function will always return null; in unspecialized app * processes this function will return a Runnable object representing the new * application that is passed up from usapMain. */ static Runnable forkUsap(LocalServerSocket usapPoolSocket, - int[] sessionSocketRawFDs) { + int[] sessionSocketRawFDs, + boolean isPriorityFork) { FileDescriptor[] pipeFDs = null; try { @@ -477,7 +486,8 @@ public final class Zygote { } int pid = - nativeForkUsap(pipeFDs[0].getInt$(), pipeFDs[1].getInt$(), sessionSocketRawFDs); + nativeForkUsap(pipeFDs[0].getInt$(), pipeFDs[1].getInt$(), + sessionSocketRawFDs, isPriorityFork); if (pid == 0) { IoUtils.closeQuietly(pipeFDs[0]); @@ -491,8 +501,9 @@ public final class Zygote { } private static native int nativeForkUsap(int readPipeFD, - int writePipeFD, - int[] sessionSocketRawFDs); + int writePipeFD, + int[] sessionSocketRawFDs, + boolean isPriorityFork); /** * This function is used by unspecialized app processes to wait for specialization requests from @@ -515,6 +526,11 @@ public final class Zygote { // Load resources ZygoteInit.nativePreloadGraphicsDriver(); + // Change the priority to max before calling accept so we can respond to new specialization + // requests as quickly as possible. This will be reverted to the default priority in the + // native specialization code. + boostUsapPriority(); + while (true) { try { sessionSocket = usapPoolSocket.accept(); @@ -618,6 +634,12 @@ public final class Zygote { null /* classLoader */); } + private static void boostUsapPriority() { + nativeBoostUsapPriority(); + } + + private static native void nativeBoostUsapPriority(); + private static final String USAP_ERROR_PREFIX = "Invalid command to USAP: "; /** @@ -858,15 +880,6 @@ public final class Zygote { } /** - * Resets the calling thread priority to the default value (Thread.NORM_PRIORITY - * or nice value 0). This updates both the priority value in java.lang.Thread and - * the nice value (setpriority). - */ - static void resetNicePriority() { - Thread.currentThread().setPriority(Thread.NORM_PRIORITY); - } - - /** * Executes "/system/bin/sh -c <command>" using the exec() system call. * This method throws a runtime exception if exec() failed, otherwise, this * method never returns. diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 785256eb6351..1a8ba7632376 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -346,7 +346,7 @@ class ZygoteConnection { if (zygoteServer.isUsapPoolEnabled()) { Runnable fpResult = zygoteServer.fillUsapPool( - new int[]{mSocket.getFileDescriptor().getInt$()}); + new int[]{mSocket.getFileDescriptor().getInt$()}, false); if (fpResult != null) { zygoteServer.setForkChild(); diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index f9e868fafe50..c5a0af75c9d9 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -822,6 +822,9 @@ public class ZygoteInit { public static void main(String argv[]) { ZygoteServer zygoteServer = null; + // Set the initial thread priority to the "normal" value. + Thread.currentThread().setPriority(Thread.NORM_PRIORITY); + // Mark zygote start. This ensures that thread creation will throw // an error. ZygoteHooks.startZygoteNoThreadCreation(); @@ -881,8 +884,6 @@ public class ZygoteInit { EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis()); bootTimingsTraceLog.traceEnd(); // ZygotePreload - } else { - Zygote.resetNicePriority(); } // Do an initial gc to clean up after startup diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java index 5d1911b9c29a..7eed9b18486a 100644 --- a/core/java/com/android/internal/os/ZygoteServer.java +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -67,6 +67,15 @@ class ZygoteServer { private static final String USAP_POOL_SIZE_MIN_DEFAULT = "1"; /** + * Number of milliseconds to delay before refilling the pool if it hasn't reached its + * minimum value. + */ + private static final int USAP_REFILL_DELAY_MS = 3000; + + /** The "not a timestamp" value for the refill delay timestamp mechanism. */ + private static final int INVALID_TIMESTAMP = -1; + + /** * Indicates if this Zygote server can support a unspecialized app process pool. Currently this * should only be true for the primary and secondary Zygotes, and not the App Zygotes or the * WebView Zygote. @@ -131,6 +140,12 @@ class ZygoteServer { */ private int mUsapPoolRefillThreshold = 0; + private enum UsapPoolRefillAction { + DELAYED, + IMMEDIATE, + NONE + } + ZygoteServer() { mUsapPoolEventFD = null; mZygoteSocket = null; @@ -293,9 +308,16 @@ class ZygoteServer { } } + private void fetchUsapPoolPolicyPropsIfUnfetched() { + if (mIsFirstPropertyCheck) { + mIsFirstPropertyCheck = false; + fetchUsapPoolPolicyProps(); + } + } + /** - * Checks to see if the current policy says that pool should be refilled, and spawns new USAPs - * if necessary. + * Refill the USAP Pool to the appropriate level, determined by whether this is a priority + * refill event or not. * * @param sessionSocketRawFDs Anonymous session sockets that are currently open * @return In the Zygote process this function will always return null; in unspecialized app @@ -303,39 +325,46 @@ class ZygoteServer { * application that is passed up from usapMain. */ - Runnable fillUsapPool(int[] sessionSocketRawFDs) { + Runnable fillUsapPool(int[] sessionSocketRawFDs, boolean isPriorityRefill) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillUsapPool"); // Ensure that the pool properties have been fetched. - fetchUsapPoolPolicyPropsWithMinInterval(); + fetchUsapPoolPolicyPropsIfUnfetched(); int usapPoolCount = Zygote.getUsapPoolCount(); - int numUsapsToSpawn = mUsapPoolSizeMax - usapPoolCount; + int numUsapsToSpawn; - if (usapPoolCount < mUsapPoolSizeMin - || numUsapsToSpawn >= mUsapPoolRefillThreshold) { + if (isPriorityRefill) { + // Refill to min + numUsapsToSpawn = mUsapPoolSizeMin - usapPoolCount; - // Disable some VM functionality and reset some system values - // before forking. - ZygoteHooks.preFork(); - Zygote.resetNicePriority(); + Log.i("zygote", + "Priority USAP Pool refill. New USAPs: " + numUsapsToSpawn); + } else { + // Refill up to max + numUsapsToSpawn = mUsapPoolSizeMax - usapPoolCount; - while (usapPoolCount++ < mUsapPoolSizeMax) { - Runnable caller = Zygote.forkUsap(mUsapPoolSocket, sessionSocketRawFDs); + Log.i("zygote", + "Delayed USAP Pool refill. New USAPs: " + numUsapsToSpawn); + } - if (caller != null) { - return caller; - } - } + // Disable some VM functionality and reset some system values + // before forking. + ZygoteHooks.preFork(); - // Re-enable runtime services for the Zygote. Services for unspecialized app process - // are re-enabled in specializeAppProcess. - ZygoteHooks.postForkCommon(); + while (--numUsapsToSpawn >= 0) { + Runnable caller = + Zygote.forkUsap(mUsapPoolSocket, sessionSocketRawFDs, isPriorityRefill); - Log.i("zygote", - "Filled the USAP pool. New USAPs: " + numUsapsToSpawn); + if (caller != null) { + return caller; + } } + // Re-enable runtime services for the Zygote. Services for unspecialized app process + // are re-enabled in specializeAppProcess. + ZygoteHooks.postForkCommon(); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); return null; @@ -358,7 +387,7 @@ class ZygoteServer { mUsapPoolEnabled = newStatus; if (newStatus) { - return fillUsapPool(new int[]{ sessionSocket.getFileDescriptor().getInt$() }); + return fillUsapPool(new int[]{ sessionSocket.getFileDescriptor().getInt$() }, false); } else { Zygote.emptyUsapPool(); return null; @@ -377,6 +406,8 @@ class ZygoteServer { socketFDs.add(mZygoteSocket.getFileDescriptor()); peers.add(null); + long usapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP; + while (true) { fetchUsapPoolPolicyPropsWithMinInterval(); @@ -428,140 +459,192 @@ class ZygoteServer { } } + int pollTimeoutMs; + + if (usapPoolRefillTriggerTimestamp == INVALID_TIMESTAMP) { + pollTimeoutMs = -1; + } else { + int elapsedTimeMs = + (int) (System.currentTimeMillis() - usapPoolRefillTriggerTimestamp); + + if (elapsedTimeMs >= USAP_REFILL_DELAY_MS) { + // Normalize the poll timeout value when the time between one poll event and the + // next pushes us over the delay value. This prevents poll receiving a 0 + // timeout value, which would result in it returning immediately. + pollTimeoutMs = -1; + } else { + pollTimeoutMs = USAP_REFILL_DELAY_MS - elapsedTimeMs; + } + } + + int pollReturnValue; try { - Os.poll(pollFDs, -1); + pollReturnValue = Os.poll(pollFDs, pollTimeoutMs); } catch (ErrnoException ex) { throw new RuntimeException("poll failed", ex); } - boolean usapPoolFDRead = false; - - while (--pollIndex >= 0) { - if ((pollFDs[pollIndex].revents & POLLIN) == 0) { - continue; - } - - if (pollIndex == 0) { - // Zygote server socket + UsapPoolRefillAction usapPoolRefillAction = UsapPoolRefillAction.NONE; + if (pollReturnValue == 0) { + // The poll timeout has been exceeded. This only occurs when we have finished the + // USAP pool refill delay period. - ZygoteConnection newPeer = acceptCommandPeer(abiList); - peers.add(newPeer); - socketFDs.add(newPeer.getFileDescriptor()); + usapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP; + usapPoolRefillAction = UsapPoolRefillAction.DELAYED; - } else if (pollIndex < usapPoolEventFDIndex) { - // Session socket accepted from the Zygote server socket + } else { + boolean usapPoolFDRead = false; - try { - ZygoteConnection connection = peers.get(pollIndex); - final Runnable command = connection.processOneCommand(this); + while (--pollIndex >= 0) { + if ((pollFDs[pollIndex].revents & POLLIN) == 0) { + continue; + } - // TODO (chriswailes): Is this extra check necessary? - if (mIsForkChild) { - // We're in the child. We should always have a command to run at this - // stage if processOneCommand hasn't called "exec". - if (command == null) { - throw new IllegalStateException("command == null"); + if (pollIndex == 0) { + // Zygote server socket + + ZygoteConnection newPeer = acceptCommandPeer(abiList); + peers.add(newPeer); + socketFDs.add(newPeer.getFileDescriptor()); + + } else if (pollIndex < usapPoolEventFDIndex) { + // Session socket accepted from the Zygote server socket + + try { + ZygoteConnection connection = peers.get(pollIndex); + final Runnable command = connection.processOneCommand(this); + + // TODO (chriswailes): Is this extra check necessary? + if (mIsForkChild) { + // We're in the child. We should always have a command to run at + // this stage if processOneCommand hasn't called "exec". + if (command == null) { + throw new IllegalStateException("command == null"); + } + + return command; + } else { + // We're in the server - we should never have any commands to run. + if (command != null) { + throw new IllegalStateException("command != null"); + } + + // We don't know whether the remote side of the socket was closed or + // not until we attempt to read from it from processOneCommand. This + // shows up as a regular POLLIN event in our regular processing + // loop. + if (connection.isClosedByPeer()) { + connection.closeSocket(); + peers.remove(pollIndex); + socketFDs.remove(pollIndex); + } } + } catch (Exception e) { + if (!mIsForkChild) { + // We're in the server so any exception here is one that has taken + // place pre-fork while processing commands or reading / writing + // from the control socket. Make a loud noise about any such + // exceptions so that we know exactly what failed and why. - return command; - } else { - // We're in the server - we should never have any commands to run. - if (command != null) { - throw new IllegalStateException("command != null"); - } + Slog.e(TAG, "Exception executing zygote command: ", e); + + // Make sure the socket is closed so that the other end knows + // immediately that something has gone wrong and doesn't time out + // waiting for a response. + ZygoteConnection conn = peers.remove(pollIndex); + conn.closeSocket(); - // We don't know whether the remote side of the socket was closed or - // not until we attempt to read from it from processOneCommand. This - // shows up as a regular POLLIN event in our regular processing loop. - if (connection.isClosedByPeer()) { - connection.closeSocket(); - peers.remove(pollIndex); socketFDs.remove(pollIndex); + } else { + // We're in the child so any exception caught here has happened post + // fork and before we execute ActivityThread.main (or any other + // main() method). Log the details of the exception and bring down + // the process. + Log.e(TAG, "Caught post-fork exception in child process.", e); + throw e; } + } finally { + // Reset the child flag, in the event that the child process is a child- + // zygote. The flag will not be consulted this loop pass after the + // Runnable is returned. + mIsForkChild = false; } - } catch (Exception e) { - if (!mIsForkChild) { - // We're in the server so any exception here is one that has taken place - // pre-fork while processing commands or reading / writing from the - // control socket. Make a loud noise about any such exceptions so that - // we know exactly what failed and why. - - Slog.e(TAG, "Exception executing zygote command: ", e); - - // Make sure the socket is closed so that the other end knows - // immediately that something has gone wrong and doesn't time out - // waiting for a response. - ZygoteConnection conn = peers.remove(pollIndex); - conn.closeSocket(); - - socketFDs.remove(pollIndex); - } else { - // We're in the child so any exception caught here has happened post - // fork and before we execute ActivityThread.main (or any other main() - // method). Log the details of the exception and bring down the process. - Log.e(TAG, "Caught post-fork exception in child process.", e); - throw e; - } - } finally { - // Reset the child flag, in the event that the child process is a child- - // zygote. The flag will not be consulted this loop pass after the Runnable - // is returned. - mIsForkChild = false; - } - } else { - // Either the USAP pool event FD or a USAP reporting pipe. - - // If this is the event FD the payload will be the number of USAPs removed. - // If this is a reporting pipe FD the payload will be the PID of the USAP - // that was just specialized. - long messagePayload = -1; - - try { - byte[] buffer = new byte[Zygote.USAP_MANAGEMENT_MESSAGE_BYTES]; - int readBytes = Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length); - - if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) { - DataInputStream inputStream = - new DataInputStream(new ByteArrayInputStream(buffer)); + } else { + // Either the USAP pool event FD or a USAP reporting pipe. + + // If this is the event FD the payload will be the number of USAPs removed. + // If this is a reporting pipe FD the payload will be the PID of the USAP + // that was just specialized. The `continue` statements below ensure that + // the messagePayload will always be valid if we complete the try block + // without an exception. + long messagePayload; + + try { + byte[] buffer = new byte[Zygote.USAP_MANAGEMENT_MESSAGE_BYTES]; + int readBytes = + Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length); + + if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) { + DataInputStream inputStream = + new DataInputStream(new ByteArrayInputStream(buffer)); + + messagePayload = inputStream.readLong(); + } else { + Log.e(TAG, "Incomplete read from USAP management FD of size " + + readBytes); + continue; + } + } catch (Exception ex) { + if (pollIndex == usapPoolEventFDIndex) { + Log.e(TAG, "Failed to read from USAP pool event FD: " + + ex.getMessage()); + } else { + Log.e(TAG, "Failed to read from USAP reporting pipe: " + + ex.getMessage()); + } - messagePayload = inputStream.readLong(); - } else { - Log.e(TAG, "Incomplete read from USAP management FD of size " - + readBytes); continue; } - } catch (Exception ex) { - if (pollIndex == usapPoolEventFDIndex) { - Log.e(TAG, "Failed to read from USAP pool event FD: " - + ex.getMessage()); - } else { - Log.e(TAG, "Failed to read from USAP reporting pipe: " - + ex.getMessage()); + + if (pollIndex > usapPoolEventFDIndex) { + Zygote.removeUsapTableEntry((int) messagePayload); } - continue; + usapPoolFDRead = true; } + } - if (pollIndex > usapPoolEventFDIndex) { - Zygote.removeUsapTableEntry((int) messagePayload); - } + if (usapPoolFDRead) { + int usapPoolCount = Zygote.getUsapPoolCount(); - usapPoolFDRead = true; + if (usapPoolCount < mUsapPoolSizeMin) { + // Immediate refill + usapPoolRefillAction = UsapPoolRefillAction.IMMEDIATE; + } else if (mUsapPoolSizeMax - usapPoolCount >= mUsapPoolRefillThreshold) { + // Delayed refill + usapPoolRefillTriggerTimestamp = System.currentTimeMillis(); + } } } - // Check to see if the USAP pool needs to be refilled. - if (usapPoolFDRead) { + if (usapPoolRefillAction != UsapPoolRefillAction.NONE) { int[] sessionSocketRawFDs = socketFDs.subList(1, socketFDs.size()) .stream() .mapToInt(fd -> fd.getInt$()) .toArray(); - final Runnable command = fillUsapPool(sessionSocketRawFDs); + final boolean isPriorityRefill = + usapPoolRefillAction == UsapPoolRefillAction.IMMEDIATE; + + final Runnable command = + fillUsapPool(sessionSocketRawFDs, isPriorityRefill); if (command != null) { return command; + } else if (isPriorityRefill) { + // Schedule a delayed refill to finish refilling the pool. + usapPoolRefillTriggerTimestamp = System.currentTimeMillis(); } } } diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 8ff16912e932..5267427a6cd3 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -169,6 +169,15 @@ static int gUsapPoolEventFD = -1; */ static constexpr int USAP_POOL_SIZE_MAX_LIMIT = 100; +/** The numeric value for the maximum priority a process may possess. */ +static constexpr int PROCESS_PRIORITY_MAX = -20; + +/** The numeric value for the minimum priority a process may possess. */ +static constexpr int PROCESS_PRIORITY_MIN = 19; + +/** The numeric value for the normal priority a process should have. */ +static constexpr int PROCESS_PRIORITY_DEFAULT = 0; + /** * A helper class containing accounting information for USAPs. */ @@ -893,7 +902,8 @@ static void ClearUsapTable() { // Utility routine to fork a process from the zygote. static pid_t ForkCommon(JNIEnv* env, bool is_system_server, const std::vector<int>& fds_to_close, - const std::vector<int>& fds_to_ignore) { + const std::vector<int>& fds_to_ignore, + bool is_priority_fork) { SetSignalHandlers(); // Curry a failure function. @@ -926,6 +936,12 @@ static pid_t ForkCommon(JNIEnv* env, bool is_system_server, pid_t pid = fork(); if (pid == 0) { + if (is_priority_fork) { + setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX); + } else { + setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MIN); + } + // The child process. PreApplicationInit(); @@ -1123,6 +1139,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags, is_system_server, is_child_zygote, managed_instruction_set); + // Reset the process priority to the default value. + setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_DEFAULT); + if (env->ExceptionCheck()) { fail_fn("Error calling post fork hooks."); } @@ -1360,7 +1379,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( fds_to_ignore.push_back(gUsapPoolEventFD); } - pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore); + pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore, true); if (pid == 0) { SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, @@ -1387,7 +1406,8 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( pid_t pid = ForkCommon(env, true, fds_to_close, - fds_to_ignore); + fds_to_ignore, + true); if (pid == 0) { SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, permitted_capabilities, effective_capabilities, @@ -1429,13 +1449,15 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( * zygote in managed code. * @param managed_session_socket_fds A list of anonymous session sockets that must be ignored by * the FD hygiene code and automatically "closed" in the new USAP. + * @param is_priority_fork Controls the nice level assigned to the newly created process * @return */ static jint com_android_internal_os_Zygote_nativeForkUsap(JNIEnv* env, jclass, jint read_pipe_fd, jint write_pipe_fd, - jintArray managed_session_socket_fds) { + jintArray managed_session_socket_fds, + jboolean is_priority_fork) { std::vector<int> fds_to_close(MakeUsapPipeReadFDVector()), fds_to_ignore(fds_to_close); @@ -1457,7 +1479,8 @@ static jint com_android_internal_os_Zygote_nativeForkUsap(JNIEnv* env, fds_to_ignore.push_back(write_pipe_fd); fds_to_ignore.insert(fds_to_ignore.end(), session_socket_fds.begin(), session_socket_fds.end()); - pid_t usap_pid = ForkCommon(env, /* is_system_server= */ false, fds_to_close, fds_to_ignore); + pid_t usap_pid = ForkCommon(env, /* is_system_server= */ false, fds_to_close, fds_to_ignore, + is_priority_fork == JNI_TRUE); if (usap_pid != 0) { ++gUsapPoolCount; @@ -1678,6 +1701,10 @@ static jboolean com_android_internal_os_Zygote_nativeDisableExecuteOnly(JNIEnv* return dl_iterate_phdr(disable_execute_only, nullptr) == 0; } +static void com_android_internal_os_Zygote_nativeBoostUsapPriority(JNIEnv* env, jclass) { + setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX); +} + static const JNINativeMethod gMethods[] = { { "nativeForkAndSpecialize", "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I", @@ -1690,7 +1717,7 @@ static const JNINativeMethod gMethods[] = { (void *) com_android_internal_os_Zygote_nativePreApplicationInit }, { "nativeInstallSeccompUidGidFilter", "(II)V", (void *) com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter }, - { "nativeForkUsap", "(II[I)I", + { "nativeForkUsap", "(II[IZ)I", (void *) com_android_internal_os_Zygote_nativeForkUsap }, { "nativeSpecializeAppProcess", "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)V", @@ -1708,7 +1735,9 @@ static const JNINativeMethod gMethods[] = { { "nativeEmptyUsapPool", "()V", (void *) com_android_internal_os_Zygote_nativeEmptyUsapPool }, { "nativeDisableExecuteOnly", "()Z", - (void *) com_android_internal_os_Zygote_nativeDisableExecuteOnly } + (void *) com_android_internal_os_Zygote_nativeDisableExecuteOnly }, + { "nativeBoostUsapPriority", "()V", + (void* ) com_android_internal_os_Zygote_nativeBoostUsapPriority } }; int register_com_android_internal_os_Zygote(JNIEnv* env) { 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/values/public.xml b/core/res/res/values/public.xml index 33bc34197851..fb54566fc959 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2889,6 +2889,99 @@ <!-- =============================================================== Resources added in version Q of the platform + =============================================================== --> + <eat-comment /> + + <public type="attr" name="packageType" id="0x01010587" /> + <public type="attr" name="opticalInsetLeft" id="0x01010588" /> + <public type="attr" name="opticalInsetTop" id="0x01010589" /> + <public type="attr" name="opticalInsetRight" id="0x0101058a" /> + <public type="attr" name="opticalInsetBottom" id="0x0101058b" /> + <public type="attr" name="forceDarkAllowed" id="0x0101058c" /> + <!-- @hide @SystemApi --> + <public type="attr" name="supportsAmbientMode" id="0x0101058d" /> + <!-- @hide For use by platform and tools only. Developers should not specify this value. --> + <public type="attr" name="usesNonSdkApi" id="0x0101058e" /> + <public type="attr" name="nonInteractiveUiTimeout" id="0x0101058f" /> + <public type="attr" name="isLightTheme" id="0x01010590" /> + <public type="attr" name="isSplitRequired" id="0x01010591" /> + <public type="attr" name="textLocale" id="0x01010592" /> + <public type="attr" name="settingsSliceUri" id="0x01010593" /> + <public type="attr" name="shell" id="0x01010594" /> + <public type="attr" name="interactiveUiTimeout" id="0x01010595" /> + <public type="attr" name="supportsMultipleDisplays" id="0x01010596" /> + <public type="attr" name="useAppZygote" id="0x01010597" /> + <public type="attr" name="selectionDividerHeight" id="0x01010598" /> + <public type="attr" name="foregroundServiceType" id="0x01010599" /> + <public type="attr" name="hasFragileUserData" id="0x0101059a" /> + <public type="attr" name="minAspectRatio" id="0x0101059b" /> + <public type="attr" name="inheritShowWhenLocked" id="0x0101059c" /> + <public type="attr" name="zygotePreloadName" id="0x0101059d" /> + <public type="attr" name="useEmbeddedDex" id="0x0101059e" /> + <public type="attr" name="forceUriPermissions" id="0x0101059f" /> + <!-- @hide @SystemApi --> + <public type="attr" name="allowClearUserDataOnFailedRestore" id="0x01010600" /> + <public type="attr" name="allowAudioPlaybackCapture" id="0x01010601" /> + <public type="attr" name="secureElementName" id="0x01010602" /> + <public type="attr" name="requestLegacyExternalStorage" id="0x01010603" /> + <public type="attr" name="enforceStatusBarContrast" id="0x01010604" /> + <public type="attr" name="enforceNavigationBarContrast" id="0x01010605" /> + <public type="attr" name="identifier" id="0x01010606" /> + + <!-- @hide @SystemApi --> + <public type="drawable" name="ic_info" id="0x010800b4" /> + + <!-- @hide @SystemApi --> + <public type="style" name="Theme.DeviceDefault.DocumentsUI" id="0x010302e2" /> + <public type="style" name="Theme.DeviceDefault.DayNight" id="0x010302e3" /> + <public type="style" name="ThemeOverlay.DeviceDefault.Accent.DayNight" id="0x010302e4" /> + + <public type="id" name="accessibilityActionPageUp" id="0x01020046" /> + <public type="id" name="accessibilityActionPageDown" id="0x01020047" /> + <public type="id" name="accessibilityActionPageLeft" id="0x01020048" /> + <public type="id" name="accessibilityActionPageRight" id="0x01020049" /> + + <!-- @hide @SystemApi --> + <public type="string" name="config_helpPackageNameKey" id="0x0104001b" /> + <!-- @hide @SystemApi --> + <public type="string" name="config_helpPackageNameValue" id="0x0104001c" /> + <!-- @hide @SystemApi --> + <public type="string" name="config_helpIntentExtraKey" id="0x0104001d" /> + <!-- @hide @SystemApi --> + <public type="string" name="config_helpIntentNameKey" id="0x0104001e" /> + <!-- @hide @SystemApi --> + <public type="string" name="config_feedbackIntentExtraKey" id="0x0104001f" /> + <!-- @hide @SystemApi --> + <public type="string" name="config_feedbackIntentNameKey" id="0x01040020" /> + <!-- @hide @SystemApi @TestApi --> + <public type="string" name="config_defaultAssistant" id="0x01040021" /> + <!-- @hide @SystemApi --> + <public type="string" name="config_defaultBrowser" id="0x01040022" /> + <!-- @hide @SystemApi @TestApi --> + <public type="string" name="config_defaultDialer" id="0x01040023" /> + <!-- @hide @SystemApi --> + <public type="string" name="config_defaultSms" id="0x01040024" /> + + <!-- @hide @SystemApi --> + <public type="bool" name="config_sendPackageName" id="0x01110000" /> + <!-- @hide @SystemApi --> + <public type="bool" name="config_showDefaultAssistant" id="0x01110001" /> + <!-- @hide @SystemApi --> + <public type="bool" name="config_showDefaultEmergency" id="0x01110002" /> + <!-- @hide @SystemApi --> + <public type="bool" name="config_showDefaultHome" id="0x01110003" /> + <!-- @hide @TestApi --> + <public type="bool" name="config_perDisplayFocusEnabled" id="0x01110004" /> + + <!-- @hide @SystemApi --> + <public type="dimen" name="config_restrictedIconSize" id="0x01050007" /> + + <!-- @hide @SystemApi --> + <public type="color" name="system_notification_accent_color" id="0x0106001c" /> + + + <!-- =============================================================== + Resources added in version R of the platform NOTE: add <public> elements within a <public-group> like so: @@ -2904,113 +2997,28 @@ =============================================================== --> <eat-comment /> - <public-group type="attr" first-id="0x01010587"> - <public name="packageType" /> - <public name="opticalInsetLeft" /> - <public name="opticalInsetTop" /> - <public name="opticalInsetRight" /> - <public name="opticalInsetBottom" /> - <public name="forceDarkAllowed" /> - <!-- @hide @SystemApi --> - <public name="supportsAmbientMode" /> - <!-- @hide For use by platform and tools only. Developers should not specify this value. --> - <public name="usesNonSdkApi" /> - <public name="nonInteractiveUiTimeout" /> - <public name="isLightTheme" /> - <public name="isSplitRequired" /> - <public name="textLocale" /> - <public name="settingsSliceUri" /> - <public name="shell" /> - <public name="interactiveUiTimeout" /> - <public name="__removed6" /> - <public name="supportsMultipleDisplays" /> - <public name="useAppZygote" /> - <public name="__removed1" /> - <public name="__removed2" /> - <public name="__removed3" /> - <public name="__removed4" /> - <public name="__removed5" /> - <public name="selectionDividerHeight" /> - <public name="foregroundServiceType" /> - <public name="hasFragileUserData" /> - <public name="minAspectRatio" /> - <public name="inheritShowWhenLocked" /> - <public name="zygotePreloadName" /> - <public name="useEmbeddedDex" /> - <public name="forceUriPermissions" /> - <!-- @hide @SystemApi --> - <public name="allowClearUserDataOnFailedRestore"/> - <public name="allowAudioPlaybackCapture"/> - <public name="secureElementName" /> - <public name="requestLegacyExternalStorage"/> - <public name="enforceStatusBarContrast" /> - <public name="enforceNavigationBarContrast" /> - <public name="identifier" /> + <public-group type="attr" first-id="0x01010607"> </public-group> - <public-group type="drawable" first-id="0x010800b4"> - <!-- @hide @SystemApi --> - <public name="ic_info" /> + <public-group type="drawable" first-id="0x010800b5"> </public-group> - <public-group type="style" first-id="0x010302e2"> - <!-- @hide @SystemApi --> - <public name="Theme.DeviceDefault.DocumentsUI" /> - <public name="Theme.DeviceDefault.DayNight" /> - <public name="ThemeOverlay.DeviceDefault.Accent.DayNight" /> + <public-group type="style" first-id="0x010302e5"> </public-group> - <public-group type="id" first-id="0x01020046"> - <public name="accessibilityActionPageUp" /> - <public name="accessibilityActionPageDown" /> - <public name="accessibilityActionPageLeft" /> - <public name="accessibilityActionPageRight" /> + <public-group type="id" first-id="0x0102004a"> </public-group> - <public-group type="string" first-id="0x0104001b"> - <!-- @hide @SystemApi --> - <public name="config_helpPackageNameKey" /> - <!-- @hide @SystemApi --> - <public name="config_helpPackageNameValue" /> - <!-- @hide @SystemApi --> - <public name="config_helpIntentExtraKey" /> - <!-- @hide @SystemApi --> - <public name="config_helpIntentNameKey" /> - <!-- @hide @SystemApi --> - <public name="config_feedbackIntentExtraKey" /> - <!-- @hide @SystemApi --> - <public name="config_feedbackIntentNameKey" /> - <!-- @hide @SystemApi @TestApi --> - <public name="config_defaultAssistant" /> - <!-- @hide @SystemApi --> - <public name="config_defaultBrowser" /> - <!-- @hide @SystemApi @TestApi --> - <public name="config_defaultDialer" /> - <!-- @hide @SystemApi --> - <public name="config_defaultSms" /> + <public-group type="string" first-id="0x01040025"> </public-group> - <public-group type="bool" first-id="0x01110000"> - <!-- @hide @SystemApi --> - <public name="config_sendPackageName" /> - <!-- @hide @SystemApi --> - <public name="config_showDefaultAssistant" /> - <!-- @hide @SystemApi --> - <public name="config_showDefaultEmergency" /> - <!-- @hide @SystemApi --> - <public name="config_showDefaultHome" /> - <!-- @hide @TestApi --> - <public name="config_perDisplayFocusEnabled" /> + <public-group type="bool" first-id="0x01110005"> </public-group> - <public-group type="dimen" first-id="0x01050007"> - <!-- @hide @SystemApi --> - <public name="config_restrictedIconSize" /> + <public-group type="dimen" first-id="0x01050008"> </public-group> - <public-group type="color" first-id="0x0106001c"> - <!-- @hide @SystemApi --> - <public name="system_notification_accent_color" /> + <public-group type="color" first-id="0x0106001d"> </public-group> <!-- =============================================================== diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 5652c85cf28f..37678dd42512 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] --> @@ -1561,7 +1561,7 @@ <!-- Error message shown when the face hardware can't be accessed. [CHAR LIMIT=69] --> <string name="face_error_hw_not_available">Can\u2019t verify face. Hardware not available.</string> <!-- Error message shown when the face hardware timer has expired and the user needs to restart the operation. [CHAR LIMIT=50] --> - <string name="face_error_timeout">Face timeout reached. Try again.</string> + <string name="face_error_timeout">Try face authentication again.</string> <!-- Error message shown when the face hardware has run out of room for storing faces. [CHAR LIMIT=69] --> <string name="face_error_no_space">Can\u2019t store new face data. Delete an old one first.</string> <!-- Generic error message shown when the face operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user. [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/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/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 8551234afa35..f326ce8d23e9 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -153,8 +153,7 @@ void CanvasContext::setSurface(sp<Surface>&& surface) { mNativeSurface = nullptr; } - if (mRenderAheadDepth == 0 && DeviceInfo::get()->getMaxRefreshRate() > 66.6f && - !mFixedRenderAhead) { + if (mRenderAheadDepth == 0 && DeviceInfo::get()->getMaxRefreshRate() > 66.6f) { mFixedRenderAhead = false; mRenderAheadCapacity = 1; } else { 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/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h index 360e8d3e8c71..60d03180c09d 100644 --- a/libs/protoutil/include/android/util/ProtoOutputStream.h +++ b/libs/protoutil/include/android/util/ProtoOutputStream.h @@ -123,6 +123,7 @@ public: size_t size(); // Get the size of the serialized protobuf. sp<ProtoReader> data(); // Get the reader apis of the data. bool flush(int fd); // Flush data directly to a file descriptor. + bool serializeToString(std::string* out); // Serializes the proto to a string. /** * Clears the ProtoOutputStream so the buffer can be reused instead of deallocation/allocation again. diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp index ccbb83b2d342..98a68c6482b5 100644 --- a/libs/protoutil/src/ProtoOutputStream.cpp +++ b/libs/protoutil/src/ProtoOutputStream.cpp @@ -448,6 +448,23 @@ ProtoOutputStream::flush(int fd) return true; } +bool +ProtoOutputStream::serializeToString(std::string* out) +{ + if (out == nullptr) return false; + if (!compact()) return false; + + + sp<ProtoReader> reader = mBuffer->read(); + out->reserve(reader->size()); + while (reader->hasNext()) { + out->append(static_cast<const char*>(static_cast<const void*>(reader->readBuffer())), + reader->currentToRead()); + reader->move(reader->currentToRead()); + } + return true; +} + sp<ProtoReader> ProtoOutputStream::data() { diff --git a/libs/protoutil/tests/ProtoOutputStream_test.cpp b/libs/protoutil/tests/ProtoOutputStream_test.cpp index 9d357f3c3363..6282fd553eae 100644 --- a/libs/protoutil/tests/ProtoOutputStream_test.cpp +++ b/libs/protoutil/tests/ProtoOutputStream_test.cpp @@ -88,6 +88,50 @@ TEST(ProtoOutputStreamTest, Primitives) { EXPECT_EQ(primitives.val_enum(), PrimitiveProto_Count_TWO); } +TEST(ProtoOutputStreamTest, SerializeToStringPrimitives) { + std::string s = "hello"; + const char b[5] = { 'a', 'p', 'p', 'l', 'e' }; + + ProtoOutputStream proto; + EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | PrimitiveProto::kValInt32FieldNumber, 123)); + EXPECT_TRUE(proto.write(FIELD_TYPE_INT64 | PrimitiveProto::kValInt64FieldNumber, -1LL)); + EXPECT_TRUE(proto.write(FIELD_TYPE_FLOAT | PrimitiveProto::kValFloatFieldNumber, -23.5f)); + EXPECT_TRUE(proto.write(FIELD_TYPE_DOUBLE | PrimitiveProto::kValDoubleFieldNumber, 324.5)); + EXPECT_TRUE(proto.write(FIELD_TYPE_UINT32 | PrimitiveProto::kValUint32FieldNumber, 3424)); + EXPECT_TRUE(proto.write(FIELD_TYPE_UINT64 | PrimitiveProto::kValUint64FieldNumber, 57LL)); + EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED32 | PrimitiveProto::kValFixed32FieldNumber, -20)); + EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED64 | PrimitiveProto::kValFixed64FieldNumber, -37LL)); + EXPECT_TRUE(proto.write(FIELD_TYPE_BOOL | PrimitiveProto::kValBoolFieldNumber, true)); + EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | PrimitiveProto::kValStringFieldNumber, s)); + EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | PrimitiveProto::kValBytesFieldNumber, b, 5)); + EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED32 | PrimitiveProto::kValSfixed32FieldNumber, 63)); + EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED64 | PrimitiveProto::kValSfixed64FieldNumber, -54)); + EXPECT_TRUE(proto.write(FIELD_TYPE_SINT32 | PrimitiveProto::kValSint32FieldNumber, -533)); + EXPECT_TRUE(proto.write(FIELD_TYPE_SINT64 | PrimitiveProto::kValSint64FieldNumber, -61224762453LL)); + EXPECT_TRUE(proto.write(FIELD_TYPE_ENUM | PrimitiveProto::kValEnumFieldNumber, 2)); + + PrimitiveProto primitives; + std::string serialized; + ASSERT_TRUE(proto.serializeToString(&serialized)); + ASSERT_TRUE(primitives.ParseFromString(serialized)); + EXPECT_EQ(primitives.val_int32(), 123); + EXPECT_EQ(primitives.val_int64(), -1); + EXPECT_EQ(primitives.val_float(), -23.5f); + EXPECT_EQ(primitives.val_double(), 324.5f); + EXPECT_EQ(primitives.val_uint32(), 3424); + EXPECT_EQ(primitives.val_uint64(), 57); + EXPECT_EQ(primitives.val_fixed32(), -20); + EXPECT_EQ(primitives.val_fixed64(), -37); + EXPECT_EQ(primitives.val_bool(), true); + EXPECT_THAT(primitives.val_string(), StrEq(s.c_str())); + EXPECT_THAT(primitives.val_bytes(), StrEq("apple")); + EXPECT_EQ(primitives.val_sfixed32(), 63); + EXPECT_EQ(primitives.val_sfixed64(), -54); + EXPECT_EQ(primitives.val_sint32(), -533); + EXPECT_EQ(primitives.val_sint64(), -61224762453LL); + EXPECT_EQ(primitives.val_enum(), PrimitiveProto_Count_TWO); +} + TEST(ProtoOutputStreamTest, Complex) { std::string name1 = "cat"; std::string name2 = "dog"; @@ -127,6 +171,47 @@ TEST(ProtoOutputStreamTest, Complex) { EXPECT_THAT(log2.data(), StrEq("food")); } +TEST(ProtoOutputStreamTest, SerializeToStringComplex) { + std::string name1 = "cat"; + std::string name2 = "dog"; + const char data1[6] = { 'f', 'u', 'n', 'n', 'y', '!' }; + const char data2[4] = { 'f', 'o', 'o', 'd' }; + + ProtoOutputStream proto; + EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 23)); + EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 101)); + EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, -72)); + uint64_t token1 = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber); + EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 12)); + EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | ComplexProto::Log::kNameFieldNumber, name1)); + // specify the length to test the write(id, bytes, length) function. + EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | ComplexProto::Log::kDataFieldNumber, data1, 5)); + proto.end(token1); + uint64_t token2 = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber); + EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 98)); + EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | ComplexProto::Log::kNameFieldNumber, name2)); + EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | ComplexProto::Log::kDataFieldNumber, data2, 4)); + proto.end(token2); + + ComplexProto complex; + std::string serialized; + ASSERT_TRUE(proto.serializeToString(&serialized)); + ASSERT_TRUE(complex.ParseFromString(serialized)); + EXPECT_EQ(complex.ints_size(), 3); + EXPECT_EQ(complex.ints(0), 23); + EXPECT_EQ(complex.ints(1), 101); + EXPECT_EQ(complex.ints(2), -72); + EXPECT_EQ(complex.logs_size(), 2); + ComplexProto::Log log1 = complex.logs(0); + EXPECT_EQ(log1.id(), 12); + EXPECT_THAT(log1.name(), StrEq(name1.c_str())); + EXPECT_THAT(log1.data(), StrEq("funny")); // should not contain '!' + ComplexProto::Log log2 = complex.logs(1); + EXPECT_EQ(log2.id(), 98); + EXPECT_THAT(log2.name(), StrEq(name2.c_str())); + EXPECT_THAT(log2.data(), StrEq("food")); +} + TEST(ProtoOutputStreamTest, Reusability) { ProtoOutputStream proto; EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 32)); 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/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index f5dab01d1b09..9b6ab06c4708 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -314,6 +314,7 @@ public class CarStatusBar extends StatusBar implements public void showKeyguard() { super.showKeyguard(); updateNavBarForKeyguardContent(); + dismissKeyguardWhenUserSwitcherNotDisplayed(); } /** diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java index 43d7d8fd0ac4..e731b45010a4 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -70,6 +70,10 @@ public class DynamicSystemInstallationService extends Service private static final String TAG = "DynSystemInstallationService"; + + // TODO (b/131866826): This is currently for test only. Will move this to System API. + static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED"; + /* * Intent actions */ @@ -122,6 +126,9 @@ public class DynamicSystemInstallationService extends Service private long mInstalledSize; private boolean mJustCancelledByUser; + // This is for testing only now + private boolean mEnableWhenCompleted; + private InstallationAsyncTask mInstallTask; @@ -178,6 +185,11 @@ public class DynamicSystemInstallationService extends Service public void onResult(int result, Throwable detail) { if (result == RESULT_OK) { postStatus(STATUS_READY, CAUSE_INSTALL_COMPLETED, null); + + // For testing: enable DSU and restart the device when install completed + if (mEnableWhenCompleted) { + executeRebootToDynSystemCommand(); + } return; } @@ -224,6 +236,7 @@ public class DynamicSystemInstallationService extends Service String url = intent.getDataString(); mSystemSize = intent.getLongExtra(DynamicSystemClient.KEY_SYSTEM_SIZE, 0); mUserdataSize = intent.getLongExtra(DynamicSystemClient.KEY_USERDATA_SIZE, 0); + mEnableWhenCompleted = intent.getBooleanExtra(KEY_ENABLE_WHEN_COMPLETED, false); mInstallTask = new InstallationAsyncTask( url, mSystemSize, mUserdataSize, this, mDynSystem, this); @@ -275,7 +288,7 @@ public class DynamicSystemInstallationService extends Service private void executeRebootToDynSystemCommand() { boolean enabled = false; - if (mInstallTask != null && mInstallTask.getStatus() == FINISHED) { + if (mInstallTask != null && mInstallTask.getResult() == RESULT_OK) { enabled = mInstallTask.commit(); } else if (isDynamicSystemInstalled()) { enabled = mDynSystem.setEnable(true); diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java index b1c09381823b..8a2948b04e3e 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java @@ -92,6 +92,8 @@ public class VerificationActivity extends Activity { Uri url = callingIntent.getData(); long systemSize = callingIntent.getLongExtra(KEY_SYSTEM_SIZE, 0); long userdataSize = callingIntent.getLongExtra(KEY_USERDATA_SIZE, 0); + boolean enableWhenCompleted = callingIntent.getBooleanExtra( + DynamicSystemInstallationService.KEY_ENABLE_WHEN_COMPLETED, false); sVerifiedUrl = url.toString(); @@ -101,6 +103,8 @@ public class VerificationActivity extends Activity { intent.setAction(DynamicSystemClient.ACTION_START_INSTALL); intent.putExtra(KEY_SYSTEM_SIZE, systemSize); intent.putExtra(KEY_USERDATA_SIZE, userdataSize); + intent.putExtra( + DynamicSystemInstallationService.KEY_ENABLE_WHEN_COMPLETED, enableWhenCompleted); Log.d(TAG, "Starting Installation Service"); startServiceAsUser(intent, UserHandle.SYSTEM); 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 c3051245e723..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 @@ -110,6 +108,8 @@ android_app { 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 @@ -118,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/apf/ApfFilter.java b/packages/NetworkStack/src/android/net/apf/ApfFilter.java index 359c85983a94..f05431968684 100644 --- a/packages/NetworkStack/src/android/net/apf/ApfFilter.java +++ b/packages/NetworkStack/src/android/net/apf/ApfFilter.java @@ -155,7 +155,8 @@ public class ApfFilter { DROPPED_ETHERTYPE_BLACKLISTED, DROPPED_ARP_REPLY_SPA_NO_HOST, DROPPED_IPV4_KEEPALIVE_ACK, - DROPPED_IPV6_KEEPALIVE_ACK; + DROPPED_IPV6_KEEPALIVE_ACK, + DROPPED_IPV4_NATT_KEEPALIVE; // Returns the negative byte offset from the end of the APF data segment for // a given counter. @@ -857,12 +858,104 @@ public class ApfFilter { } } - // A class to hold keepalive ack information. - private abstract static class TcpKeepaliveAck { + // TODO: Refactor these subclasses to avoid so much repetition. + private abstract static class KeepalivePacket { // Note that the offset starts from IP header. // These must be added ether header length when generating program. static final int IP_HEADER_OFFSET = 0; + static final int IPV4_SRC_ADDR_OFFSET = IP_HEADER_OFFSET + 12; + // Append a filter for this keepalive ack to {@code gen}. + // Jump to drop if it matches the keepalive ack. + // Jump to the next filter if packet doesn't match the keepalive ack. + abstract void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException; + } + + // A class to hold NAT-T keepalive ack information. + private class NattKeepaliveResponse extends KeepalivePacket { + static final int UDP_LENGTH_OFFSET = 4; + static final int UDP_HEADER_LEN = 8; + + protected class NattKeepaliveResponseData { + public final byte[] srcAddress; + public final int srcPort; + public final byte[] dstAddress; + public final int dstPort; + + NattKeepaliveResponseData(final NattKeepalivePacketDataParcelable sentKeepalivePacket) { + srcAddress = sentKeepalivePacket.dstAddress; + srcPort = sentKeepalivePacket.dstPort; + dstAddress = sentKeepalivePacket.srcAddress; + dstPort = sentKeepalivePacket.srcPort; + } + } + + protected final NattKeepaliveResponseData mPacket; + protected final byte[] mSrcDstAddr; + protected final byte[] mPortFingerprint; + // NAT-T keepalive packet + protected final byte[] mPayload = {(byte) 0xff}; + + NattKeepaliveResponse(final NattKeepalivePacketDataParcelable sentKeepalivePacket) { + mPacket = new NattKeepaliveResponseData(sentKeepalivePacket); + mSrcDstAddr = concatArrays(mPacket.srcAddress, mPacket.dstAddress); + mPortFingerprint = generatePortFingerprint(mPacket.srcPort, mPacket.dstPort); + } + + byte[] generatePortFingerprint(int srcPort, int dstPort) { + final ByteBuffer fp = ByteBuffer.allocate(4); + fp.order(ByteOrder.BIG_ENDIAN); + fp.putShort((short) srcPort); + fp.putShort((short) dstPort); + return fp.array(); + } + + @Override + void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException { + final String nextFilterLabel = "natt_keepalive_filter" + getUniqueNumberLocked(); + + gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET); + gen.addJumpIfBytesNotEqual(Register.R0, mSrcDstAddr, nextFilterLabel); + + // A NAT-T keepalive packet contains 1 byte payload with the value 0xff + // Check payload length is 1 + gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addAdd(UDP_HEADER_LEN); + gen.addSwap(); + gen.addLoad16(Register.R0, IPV4_TOTAL_LENGTH_OFFSET); + gen.addNeg(Register.R1); + gen.addAddR1(); + gen.addJumpIfR0NotEquals(1, nextFilterLabel); + + // Check that the ports match + gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addAdd(ETH_HEADER_LEN); + gen.addJumpIfBytesNotEqual(Register.R0, mPortFingerprint, nextFilterLabel); + + // Payload offset = R0 + UDP header length + gen.addAdd(UDP_HEADER_LEN); + gen.addJumpIfBytesNotEqual(Register.R0, mPayload, nextFilterLabel); + + maybeSetupCounter(gen, Counter.DROPPED_IPV4_NATT_KEEPALIVE); + gen.addJump(mCountAndDropLabel); + gen.defineLabel(nextFilterLabel); + } + + public String toString() { + try { + return String.format("%s -> %s", + NetworkStackUtils.addressAndPortToString( + InetAddress.getByAddress(mPacket.srcAddress), mPacket.srcPort), + NetworkStackUtils.addressAndPortToString( + InetAddress.getByAddress(mPacket.dstAddress), mPacket.dstPort)); + } catch (UnknownHostException e) { + return "Unknown host"; + } + } + } + + // A class to hold TCP keepalive ack information. + private abstract static class TcpKeepaliveAck extends KeepalivePacket { protected static class TcpKeepaliveAckData { public final byte[] srcAddress; public final int srcPort; @@ -870,6 +963,7 @@ public class ApfFilter { public final int dstPort; public final int seq; public final int ack; + // Create the characteristics of the ack packet from the sent keepalive packet. TcpKeepaliveAckData(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) { srcAddress = sentKeepalivePacket.dstAddress; @@ -902,28 +996,18 @@ public class ApfFilter { return fp.array(); } - static byte[] concatArrays(final byte[]... arr) { - int size = 0; - for (byte[] a : arr) { - size += a.length; - } - final byte[] result = new byte[size]; - int offset = 0; - for (byte[] a : arr) { - System.arraycopy(a, 0, result, offset, a.length); - offset += a.length; - } - return result; - } - public String toString() { - return String.format("%s(%d) -> %s(%d), seq=%d, ack=%d", - mPacket.srcAddress, - mPacket.srcPort, - mPacket.dstAddress, - mPacket.dstPort, - mPacket.seq, - mPacket.ack); + try { + return String.format("%s -> %s , seq=%d, ack=%d", + NetworkStackUtils.addressAndPortToString( + InetAddress.getByAddress(mPacket.srcAddress), mPacket.srcPort), + NetworkStackUtils.addressAndPortToString( + InetAddress.getByAddress(mPacket.dstAddress), mPacket.dstPort), + Integer.toUnsignedLong(mPacket.seq), + Integer.toUnsignedLong(mPacket.ack)); + } catch (UnknownHostException e) { + return "Unknown host"; + } } // Append a filter for this keepalive ack to {@code gen}. @@ -933,7 +1017,6 @@ public class ApfFilter { } private class TcpKeepaliveAckV4 extends TcpKeepaliveAck { - private static final int IPV4_SRC_ADDR_OFFSET = IP_HEADER_OFFSET + 12; TcpKeepaliveAckV4(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) { this(new TcpKeepaliveAckData(sentKeepalivePacket)); @@ -987,7 +1070,7 @@ public class ApfFilter { @Override void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException { - throw new UnsupportedOperationException("IPv6 Keepalive is not supported yet"); + throw new UnsupportedOperationException("IPv6 TCP Keepalive is not supported yet"); } } @@ -997,7 +1080,7 @@ public class ApfFilter { @GuardedBy("this") private ArrayList<Ra> mRas = new ArrayList<>(); @GuardedBy("this") - private SparseArray<TcpKeepaliveAck> mKeepaliveAcks = new SparseArray<>(); + private SparseArray<KeepalivePacket> mKeepalivePackets = new SparseArray<>(); // There is always some marginal benefit to updating the installed APF program when an RA is // seen because we can extend the program's lifetime slightly, but there is some cost to @@ -1171,9 +1254,12 @@ public class ApfFilter { gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel); } - // If any keepalive filter matches, drop + // If any TCP keepalive filter matches, drop generateV4KeepaliveFilters(gen); + // If any NAT-T keepalive filter matches, drop + generateV4NattKeepaliveFilters(gen); + // Otherwise, this is an IPv4 unicast, pass // If L2 broadcast packet, drop. // TODO: can we invert this condition to fall through to the common pass case below? @@ -1184,6 +1270,7 @@ public class ApfFilter { gen.addJump(mCountAndDropLabel); } else { generateV4KeepaliveFilters(gen); + generateV4NattKeepaliveFilters(gen); } // Otherwise, pass @@ -1191,25 +1278,36 @@ public class ApfFilter { gen.addJump(mCountAndPassLabel); } - private void generateV4KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException { - final String skipV4KeepaliveFilter = "skip_v4_keepalive_filter"; - final boolean haveV4KeepaliveAcks = NetworkStackUtils.any(mKeepaliveAcks, - ack -> ack instanceof TcpKeepaliveAckV4); + private void generateKeepaliveFilters(ApfGenerator gen, Class<?> filterType, int proto, + int offset, String label) throws IllegalInstructionException { + final boolean haveKeepaliveResponses = NetworkStackUtils.any(mKeepalivePackets, + ack -> filterType.isInstance(ack)); - // If no keepalive acks - if (!haveV4KeepaliveAcks) return; + // If no keepalive packets of this type + if (!haveKeepaliveResponses) return; - // If not tcp, skip keepalive filters - gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET); - gen.addJumpIfR0NotEquals(IPPROTO_TCP, skipV4KeepaliveFilter); + // If not the right proto, skip keepalive filters + gen.addLoad8(Register.R0, offset); + gen.addJumpIfR0NotEquals(proto, label); - // Drop IPv4 Keepalive acks - for (int i = 0; i < mKeepaliveAcks.size(); ++i) { - final TcpKeepaliveAck ack = mKeepaliveAcks.valueAt(i); - if (ack instanceof TcpKeepaliveAckV4) ack.generateFilterLocked(gen); + // Drop Keepalive responses + for (int i = 0; i < mKeepalivePackets.size(); ++i) { + final KeepalivePacket response = mKeepalivePackets.valueAt(i); + if (filterType.isInstance(response)) response.generateFilterLocked(gen); } - gen.defineLabel(skipV4KeepaliveFilter); + gen.defineLabel(label); + } + + private void generateV4KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException { + generateKeepaliveFilters(gen, TcpKeepaliveAckV4.class, IPPROTO_TCP, IPV4_PROTOCOL_OFFSET, + "skip_v4_keepalive_filter"); + } + + private void generateV4NattKeepaliveFilters(ApfGenerator gen) + throws IllegalInstructionException { + generateKeepaliveFilters(gen, NattKeepaliveResponse.class, + IPPROTO_UDP, IPV4_PROTOCOL_OFFSET, "skip_v4_nattkeepalive_filter"); } /** @@ -1294,24 +1392,8 @@ public class ApfFilter { } private void generateV6KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException { - final String skipV6KeepaliveFilter = "skip_v6_keepalive_filter"; - final boolean haveV6KeepaliveAcks = NetworkStackUtils.any(mKeepaliveAcks, - ack -> ack instanceof TcpKeepaliveAckV6); - - // If no keepalive acks - if (!haveV6KeepaliveAcks) return; - - // If not tcp, skip keepalive filters - gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET); - gen.addJumpIfR0NotEquals(IPPROTO_TCP, skipV6KeepaliveFilter); - - // Drop IPv6 Keepalive acks - for (int i = 0; i < mKeepaliveAcks.size(); ++i) { - final TcpKeepaliveAck ack = mKeepaliveAcks.valueAt(i); - if (ack instanceof TcpKeepaliveAckV6) ack.generateFilterLocked(gen); - } - - gen.defineLabel(skipV6KeepaliveFilter); + generateKeepaliveFilters(gen, TcpKeepaliveAckV6.class, IPPROTO_TCP, IPV6_NEXT_HEADER_OFFSET, + "skip_v6_keepalive_filter"); } /** @@ -1701,26 +1783,34 @@ public class ApfFilter { public synchronized void addTcpKeepalivePacketFilter(final int slot, final TcpKeepalivePacketDataParcelable sentKeepalivePacket) { log("Adding keepalive ack(" + slot + ")"); - if (null != mKeepaliveAcks.get(slot)) { + if (null != mKeepalivePackets.get(slot)) { throw new IllegalArgumentException("Keepalive slot " + slot + " is occupied"); } final int ipVersion = sentKeepalivePacket.srcAddress.length == 4 ? 4 : 6; - mKeepaliveAcks.put(slot, (ipVersion == 4) + mKeepalivePackets.put(slot, (ipVersion == 4) ? new TcpKeepaliveAckV4(sentKeepalivePacket) : new TcpKeepaliveAckV6(sentKeepalivePacket)); installNewProgramLocked(); } /** - * Add NATT keepalive packet filter. - * This will add a filter to drop NATT keepalive packet which is passed as an argument. + * Add NAT-T keepalive packet filter. + * This will add a filter to drop NAT-T keepalive packet which is passed as an argument. * * @param slot The index used to access the filter. * @param sentKeepalivePacket The attributes of the sent keepalive packet. */ public synchronized void addNattKeepalivePacketFilter(final int slot, final NattKeepalivePacketDataParcelable sentKeepalivePacket) { - Log.e(TAG, "APF add NATT keepalive filter is not implemented"); + log("Adding NAT-T keepalive packet(" + slot + ")"); + if (null != mKeepalivePackets.get(slot)) { + throw new IllegalArgumentException("NAT-T Keepalive slot " + slot + " is occupied"); + } + if (sentKeepalivePacket.srcAddress.length != 4) { + throw new IllegalArgumentException("NAT-T keepalive is only supported on IPv4"); + } + mKeepalivePackets.put(slot, new NattKeepaliveResponse(sentKeepalivePacket)); + installNewProgramLocked(); } /** @@ -1729,7 +1819,8 @@ public class ApfFilter { * @param slot The index used to access the filter. */ public synchronized void removeKeepalivePacketFilter(int slot) { - mKeepaliveAcks.remove(slot); + log("Removing keepalive packet(" + slot + ")"); + mKeepalivePackets.remove(slot); installNewProgramLocked(); } @@ -1785,14 +1876,29 @@ public class ApfFilter { } pw.decreaseIndent(); - pw.println("Keepalive filters:"); + pw.println("TCP Keepalive filters:"); pw.increaseIndent(); - for (int i = 0; i < mKeepaliveAcks.size(); ++i) { - final TcpKeepaliveAck keepaliveAck = mKeepaliveAcks.valueAt(i); - pw.print("Slot "); - pw.print(mKeepaliveAcks.keyAt(i)); - pw.print(" : "); - pw.println(keepaliveAck); + for (int i = 0; i < mKeepalivePackets.size(); ++i) { + final KeepalivePacket keepalivePacket = mKeepalivePackets.valueAt(i); + if (keepalivePacket instanceof TcpKeepaliveAck) { + pw.print("Slot "); + pw.print(mKeepalivePackets.keyAt(i)); + pw.print(": "); + pw.println(keepalivePacket); + } + } + pw.decreaseIndent(); + + pw.println("NAT-T Keepalive filters:"); + pw.increaseIndent(); + for (int i = 0; i < mKeepalivePackets.size(); ++i) { + final KeepalivePacket keepalivePacket = mKeepalivePackets.valueAt(i); + if (keepalivePacket instanceof NattKeepaliveResponse) { + pw.print("Slot "); + pw.print(mKeepalivePackets.keyAt(i)); + pw.print(": "); + pw.println(keepalivePacket); + } } pw.decreaseIndent(); @@ -1858,4 +1964,18 @@ public class ApfFilter { + (uint8(bytes[2]) << 8) + (uint8(bytes[3])); } + + private static byte[] concatArrays(final byte[]... arr) { + int size = 0; + for (byte[] a : arr) { + size += a.length; + } + final byte[] result = new byte[size]; + int offset = 0; + for (byte[] a : arr) { + System.arraycopy(a, 0, result, offset, a.length); + offset += a.length; + } + return result; + } } diff --git a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java index 2934e1cf0c82..9bf1b967e397 100644 --- a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java +++ b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java @@ -24,6 +24,8 @@ import android.util.SparseArray; import java.io.FileDescriptor; import java.io.IOException; import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; import java.net.SocketException; import java.util.List; import java.util.function.Predicate; @@ -228,4 +230,13 @@ public class NetworkStackUtils { private static native void addArpEntry(byte[] ethAddr, byte[] netAddr, String ifname, FileDescriptor fd) throws IOException; + + /** + * Return IP address and port in a string format. + */ + public static String addressAndPortToString(InetAddress address, int port) { + return String.format( + (address instanceof Inet6Address) ? "[%s]:%d" : "%s:%d", + address.getHostAddress(), port); + } } 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/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java index 93ab3be28fc7..8f2b96807860 100644 --- a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java +++ b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java @@ -40,6 +40,7 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.NattKeepalivePacketDataParcelable; import android.net.TcpKeepalivePacketDataParcelable; import android.net.apf.ApfFilter.ApfConfiguration; import android.net.apf.ApfGenerator.IllegalInstructionException; @@ -998,47 +999,54 @@ public class ApfTest { } } - private static final int ETH_HEADER_LEN = 14; - private static final int ETH_DEST_ADDR_OFFSET = 0; - private static final int ETH_ETHERTYPE_OFFSET = 12; + private static final int ETH_HEADER_LEN = 14; + private static final int ETH_DEST_ADDR_OFFSET = 0; + private static final int ETH_ETHERTYPE_OFFSET = 12; private static final byte[] ETH_BROADCAST_MAC_ADDRESS = {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; - private static final int IPV4_HEADER_LEN = 20; - private static final int IPV4_VERSION_IHL_OFFSET = ETH_HEADER_LEN + 0; + private static final int IPV4_HEADER_LEN = 20; + private static final int IPV4_VERSION_IHL_OFFSET = ETH_HEADER_LEN + 0; private static final int IPV4_TOTAL_LENGTH_OFFSET = ETH_HEADER_LEN + 2; - private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9; - private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12; - private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16; - private static final int IPV4_TCP_HEADER_LEN = 20; - private static final int IPV4_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN; - private static final int IPV4_TCP_SRC_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 0; - private static final int IPV4_TCP_DEST_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 2; - private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4; - private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8; + private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9; + private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12; + private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16; + + private static final int IPV4_TCP_HEADER_LEN = 20; + private static final int IPV4_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN; + private static final int IPV4_TCP_SRC_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 0; + private static final int IPV4_TCP_DEST_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 2; + private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4; + private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8; private static final int IPV4_TCP_HEADER_LENGTH_OFFSET = IPV4_TCP_HEADER_OFFSET + 12; - private static final int IPV4_TCP_HEADER_FLAG_OFFSET = IPV4_TCP_HEADER_OFFSET + 13; + private static final int IPV4_TCP_HEADER_FLAG_OFFSET = IPV4_TCP_HEADER_OFFSET + 13; + + private static final int IPV4_UDP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN;; + private static final int IPV4_UDP_SRC_PORT_OFFSET = IPV4_UDP_HEADER_OFFSET + 0; + private static final int IPV4_UDP_DEST_PORT_OFFSET = IPV4_UDP_HEADER_OFFSET + 2; + private static final int IPV4_UDP_LENGTH_OFFSET = IPV4_UDP_HEADER_OFFSET + 4; + private static final int IPV4_UDP_PAYLOAD_OFFSET = IPV4_UDP_HEADER_OFFSET + 8; private static final byte[] IPV4_BROADCAST_ADDRESS = {(byte) 255, (byte) 255, (byte) 255, (byte) 255}; - private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6; - private static final int IPV6_HEADER_LEN = 40; - private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8; - private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24; - private static final int IPV6_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN; - private static final int IPV6_TCP_SRC_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 0; - private static final int IPV6_TCP_DEST_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 2; - private static final int IPV6_TCP_SEQ_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 4; - private static final int IPV6_TCP_ACK_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 8; + private static final int IPV6_HEADER_LEN = 40; + private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6; + private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8; + private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24; + private static final int IPV6_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN; + private static final int IPV6_TCP_SRC_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 0; + private static final int IPV6_TCP_DEST_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 2; + private static final int IPV6_TCP_SEQ_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 4; + private static final int IPV6_TCP_ACK_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 8; // The IPv6 all nodes address ff02::1 - private static final byte[] IPV6_ALL_NODES_ADDRESS = + private static final byte[] IPV6_ALL_NODES_ADDRESS = { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; private static final byte[] IPV6_ALL_ROUTERS_ADDRESS = { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 }; - private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN; - private static final int ICMP6_ROUTER_SOLICITATION = 133; - private static final int ICMP6_ROUTER_ADVERTISEMENT = 134; + private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN; + private static final int ICMP6_ROUTER_SOLICITATION = 133; + private static final int ICMP6_ROUTER_ADVERTISEMENT = 134; private static final int ICMP6_NEIGHBOR_SOLICITATION = 135; private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136; @@ -1050,9 +1058,9 @@ public class ApfTest { private static final int ICMP6_RA_OPTION_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN; - private static final int ICMP6_PREFIX_OPTION_TYPE = 3; - private static final int ICMP6_PREFIX_OPTION_LEN = 32; - private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4; + private static final int ICMP6_PREFIX_OPTION_TYPE = 3; + private static final int ICMP6_PREFIX_OPTION_LEN = 32; + private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4; private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET = 8; // From RFC6106: Recursive DNS Server option @@ -1063,17 +1071,17 @@ public class ApfTest { // From RFC4191: Route Information option private static final int ICMP6_ROUTE_INFO_OPTION_TYPE = 24; // Above three options all have the same format: - private static final int ICMP6_4_BYTE_OPTION_LEN = 8; + private static final int ICMP6_4_BYTE_OPTION_LEN = 8; private static final int ICMP6_4_BYTE_LIFETIME_OFFSET = 4; - private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4; + private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4; - private static final int UDP_HEADER_LEN = 8; + private static final int UDP_HEADER_LEN = 8; private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 22; - private static final int DHCP_CLIENT_PORT = 68; + private static final int DHCP_CLIENT_PORT = 68; private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 48; - private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN; + private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN; private static final byte[] ARP_IPV4_REQUEST_HEADER = { 0, 1, // Hardware type: Ethernet (1) 8, 0, // Protocol type: IP (0x0800) @@ -1714,6 +1722,83 @@ public class ApfTest { return packet.array(); } + @Test + public void testApfFilterNattKeepalivePacket() throws Exception { + final MockIpClientCallback cb = new MockIpClientCallback(); + final ApfConfiguration config = getDefaultConfig(); + config.multicastFilter = DROP_MULTICAST; + config.ieee802_3Filter = DROP_802_3_FRAMES; + final TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog); + byte[] program; + final int srcPort = 1024; + final int dstPort = 4500; + final int slot1 = 1; + // NAT-T keepalive + final byte[] kaPayload = {(byte) 0xff}; + final byte[] nonKaPayload = {(byte) 0xfe}; + + // src: 10.0.0.5, port: 1024 + // dst: 10.0.0.6, port: 4500 + InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR); + InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR); + + final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable(); + parcel.srcAddress = srcAddr.getAddress(); + parcel.srcPort = srcPort; + parcel.dstAddress = dstAddr.getAddress(); + parcel.dstPort = dstPort; + + apfFilter.addNattKeepalivePacketFilter(slot1, parcel); + program = cb.getApfProgram(); + + // Verify IPv4 keepalive packet is dropped + // src: 10.0.0.6, port: 4500 + // dst: 10.0.0.5, port: 1024 + byte[] pkt = ipv4UdpPacket(IPV4_KEEPALIVE_DST_ADDR, + IPV4_KEEPALIVE_SRC_ADDR, dstPort, srcPort, 1 /* dataLength */); + System.arraycopy(kaPayload, 0, pkt, IPV4_UDP_PAYLOAD_OFFSET, kaPayload.length); + assertDrop(program, pkt); + + // Verify a packet with payload length 1 byte but it is not 0xff will pass the filter. + System.arraycopy(nonKaPayload, 0, pkt, IPV4_UDP_PAYLOAD_OFFSET, nonKaPayload.length); + assertPass(program, pkt); + + // Verify IPv4 non-keepalive response packet from the same source address is passed + assertPass(program, + ipv4UdpPacket(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, 10 /* dataLength */)); + + // Verify IPv4 non-keepalive response packet from other source address is passed + assertPass(program, + ipv4UdpPacket(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, + dstPort, srcPort, 10 /* dataLength */)); + + apfFilter.removeKeepalivePacketFilter(slot1); + apfFilter.shutdown(); + } + + private static byte[] ipv4UdpPacket(byte[] sip, byte[] dip, int sport, + int dport, int dataLength) { + final int totalLength = dataLength + IPV4_HEADER_LEN + UDP_HEADER_LEN; + final int udpLength = UDP_HEADER_LEN + dataLength; + ByteBuffer packet = ByteBuffer.wrap(new byte[totalLength + ETH_HEADER_LEN]); + + // ether type + packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP); + + // IPv4 header + packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45); + packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength); + packet.put(IPV4_PROTOCOL_OFFSET, (byte) IPPROTO_UDP); + put(packet, IPV4_SRC_ADDR_OFFSET, sip); + put(packet, IPV4_DEST_ADDR_OFFSET, dip); + packet.putShort(IPV4_UDP_SRC_PORT_OFFSET, (short) sport); + packet.putShort(IPV4_UDP_DEST_PORT_OFFSET, (short) dport); + packet.putShort(IPV4_UDP_LENGTH_OFFSET, (short) udpLength); + + return packet.array(); + } + // Verify that the last program pushed to the IpClient.Callback properly filters the // given packet for the given lifetime. private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) { 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/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 05af4e1f80b3..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) { @@ -1243,6 +1254,14 @@ public class AccessPoint implements Comparable<AccessPoint> { 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/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/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/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-land/global_actions_grid.xml b/packages/SystemUI/res/layout-land/global_actions_grid.xml index 511910ea2f61..86b103d63f1a 100644 --- a/packages/SystemUI/res/layout-land/global_actions_grid.xml +++ b/packages/SystemUI/res/layout-land/global_actions_grid.xml @@ -5,19 +5,21 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" - android:clipToPadding="false" android:theme="@style/qs_theme" - android:paddingLeft="@dimen/global_actions_top_padding" android:gravity="right" android:clipChildren="false" + android:clipToPadding="false" + android:paddingRight="@dimen/global_actions_grid_container_shadow_offset" + android:layout_marginRight="@dimen/global_actions_grid_container_negative_shadow_offset" > <LinearLayout android:layout_height="match_parent" android:layout_width="wrap_content" android:gravity="top|right" - android:padding="0dp" android:orientation="vertical" android:layout_marginRight="@dimen/global_actions_grid_container_bottom_margin" + android:clipChildren="false" + android:clipToPadding="false" > <!-- Grid of action items --> <com.android.systemui.globalactions.ListGridLayout diff --git a/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml b/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml index ff2a7d8d6faa..4c3fcc0c87a0 100644 --- a/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml +++ b/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml @@ -5,11 +5,12 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" - android:clipToPadding="false" android:theme="@style/qs_theme" android:gravity="left" - android:paddingRight="@dimen/global_actions_top_padding" android:clipChildren="false" + android:clipToPadding="false" + android:paddingLeft="@dimen/global_actions_grid_container_shadow_offset" + android:layout_marginLeft="@dimen/global_actions_grid_container_negative_shadow_offset" > <LinearLayout android:layout_height="match_parent" @@ -17,6 +18,8 @@ android:gravity="bottom|left" android:padding="0dp" android:orientation="vertical" + android:clipChildren="false" + android:clipToPadding="false" android:layout_marginLeft="@dimen/global_actions_grid_container_bottom_margin" > <!-- For separated items--> 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/global_actions_grid.xml b/packages/SystemUI/res/layout/global_actions_grid.xml index 3f10b388fdd5..43e6b49b4248 100644 --- a/packages/SystemUI/res/layout/global_actions_grid.xml +++ b/packages/SystemUI/res/layout/global_actions_grid.xml @@ -5,17 +5,20 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" - android:clipToPadding="false" android:theme="@style/qs_theme" android:gravity="bottom" android:clipChildren="false" + android:clipToPadding="false" + android:paddingBottom="@dimen/global_actions_grid_container_shadow_offset" + android:layout_marginBottom="@dimen/global_actions_grid_container_negative_shadow_offset" > <LinearLayout android:layout_height="wrap_content" android:layout_width="match_parent" android:gravity="bottom | right" - android:padding="0dp" android:layoutDirection="ltr" + android:clipChildren="false" + android:clipToPadding="false" android:layout_marginBottom="@dimen/global_actions_grid_container_bottom_margin" > <!-- For separated items--> 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/values-sw320dp/dimens.xml b/packages/SystemUI/res/values-sw320dp/dimens.xml index 8f27f45c5396..4390d35e2ad2 100644 --- a/packages/SystemUI/res/values-sw320dp/dimens.xml +++ b/packages/SystemUI/res/values-sw320dp/dimens.xml @@ -16,8 +16,6 @@ --> <resources> <!-- Global actions grid --> - <dimen name="global_actions_grid_container_bottom_margin">4dp</dimen> - <dimen name="global_actions_grid_vertical_padding">0dp</dimen> <dimen name="global_actions_grid_horizontal_padding">3dp</dimen> diff --git a/packages/SystemUI/res/values-sw392dp/dimens.xml b/packages/SystemUI/res/values-sw392dp/dimens.xml index 6fa6692c4d90..2557ff4bb578 100644 --- a/packages/SystemUI/res/values-sw392dp/dimens.xml +++ b/packages/SystemUI/res/values-sw392dp/dimens.xml @@ -16,8 +16,6 @@ --> <resources> <!-- Global actions grid --> - <dimen name="global_actions_grid_container_bottom_margin">4dp</dimen> - <dimen name="global_actions_grid_vertical_padding">0dp</dimen> <dimen name="global_actions_grid_horizontal_padding">3dp</dimen> diff --git a/packages/SystemUI/res/values-sw410dp/dimens.xml b/packages/SystemUI/res/values-sw410dp/dimens.xml index fea1ef11e139..6780dca130b8 100644 --- a/packages/SystemUI/res/values-sw410dp/dimens.xml +++ b/packages/SystemUI/res/values-sw410dp/dimens.xml @@ -23,8 +23,6 @@ <dimen name="qs_detail_items_padding_top">16dp</dimen> <!-- Global actions grid --> - <dimen name="global_actions_grid_container_bottom_margin">4dp</dimen> - <dimen name="global_actions_grid_vertical_padding">8dp</dimen> <dimen name="global_actions_grid_horizontal_padding">4dp</dimen> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 62974238cd9f..6dbc3854b57c 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -944,6 +944,12 @@ <!-- Global actions grid layout --> <dimen name="global_actions_grid_side_margin">4dp</dimen> + <dimen name="global_actions_grid_container_bottom_margin">4dp</dimen> + + <!-- Used to workaround a bug where shadows are clipped during animations by expanding + the bounds of the parent view. --> + <dimen name="global_actions_grid_container_shadow_offset">20dp</dimen> + <dimen name="global_actions_grid_container_negative_shadow_offset">-20dp</dimen> <!-- The maximum offset in either direction that elements are moved horizontally to prevent burn-in on AOD. --> 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/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java index 55499dab05f3..0e91e4109ec0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java @@ -68,6 +68,9 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit private final AppearAnimationUtils mAppearAnimationUtils; private final DisappearAnimationUtils mDisappearAnimationUtils; private final DisappearAnimationUtils mDisappearAnimationUtilsLocked; + private final int[] mTmpPosition = new int[2]; + private final Rect mTempRect = new Rect(); + private final Rect mLockPatternScreenBounds = new Rect(); private CountDownTimer mCountdownTimer = null; private LockPatternUtils mLockPatternUtils; @@ -92,7 +95,6 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit mLockPatternView.clearPattern(); } }; - private Rect mTempRect = new Rect(); @VisibleForTesting KeyguardMessageArea mSecurityMessageDisplay; private View mEcaView; @@ -199,6 +201,15 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit } @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + mLockPatternView.getLocationOnScreen(mTmpPosition); + mLockPatternScreenBounds.set(mTmpPosition[0], mTmpPosition[1], + mTmpPosition[0] + mLockPatternView.getWidth(), + mTmpPosition[1] + mLockPatternView.getHeight()); + } + + @Override public void reset() { // reset lock pattern mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled( @@ -233,9 +244,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit @Override public boolean disallowInterceptTouch(MotionEvent event) { - mTempRect.set(mLockPatternView.getLeft(), mLockPatternView.getTop(), - mLockPatternView.getRight(), mLockPatternView.getBottom()); - return mTempRect.contains((int) event.getX(), (int) event.getY()); + return mLockPatternScreenBounds.contains((int) event.getRawX(), (int) event.getRawY()); } /** TODO: hook this up */ 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..e66a8fa96298 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java @@ -31,7 +31,9 @@ import android.util.Log; import android.view.WindowManager; import com.android.internal.os.SomeArgs; +import com.android.systemui.Dependency; import com.android.systemui.SystemUI; +import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.statusbar.CommandQueue; /** @@ -58,6 +60,7 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba private IBiometricServiceReceiverInternal mReceiver; private boolean mDialogShowing; private Callback mCallback = new Callback(); + private WakefulnessLifecycle mWakefulnessLifecycle; private Handler mHandler = new Handler(Looper.getMainLooper()) { @Override @@ -133,6 +136,16 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba } } + final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() { + @Override + public void onStartedGoingToSleep() { + if (mDialogShowing) { + if (DEBUG) Log.d(TAG, "User canceled due to screen off"); + mHandler.obtainMessage(MSG_USER_CANCELED).sendToTarget(); + } + } + }; + @Override public void start() { final PackageManager pm = mContext.getPackageManager(); @@ -141,6 +154,8 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba || pm.hasSystemFeature(PackageManager.FEATURE_IRIS)) { getComponent(CommandQueue.class).addCallback(this); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class); + mWakefulnessLifecycle.addObserver(mWakefulnessObserver); } } @@ -325,7 +340,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..f60e95e32600 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -18,6 +18,9 @@ package com.android.systemui.bubbles; import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.os.UserHandle; import android.view.LayoutInflater; @@ -37,6 +40,7 @@ class Bubble { private final String mKey; private final String mGroupId; + private String mAppName; private final BubbleExpandedView.OnBubbleBlockedListener mListener; private boolean mInflated; @@ -45,6 +49,7 @@ class Bubble { BubbleExpandedView expandedView; private long mLastUpdated; private long mLastAccessed; + private PackageManager mPm; public static String groupId(NotificationEntry entry) { UserHandle user = entry.notification.getUser(); @@ -53,16 +58,33 @@ class Bubble { /** Used in tests when no UI is required. */ @VisibleForTesting(visibility = PRIVATE) - Bubble(NotificationEntry e) { - this (e, null); + Bubble(Context context, NotificationEntry e) { + this (context, e, null); } - Bubble(NotificationEntry e, BubbleExpandedView.OnBubbleBlockedListener listener) { + Bubble(Context context, NotificationEntry e, + BubbleExpandedView.OnBubbleBlockedListener listener) { entry = e; mKey = e.key; mLastUpdated = e.notification.getPostTime(); mGroupId = groupId(e); mListener = listener; + + mPm = context.getPackageManager(); + ApplicationInfo info; + try { + info = mPm.getApplicationInfo( + entry.notification.getPackageName(), + PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DISABLED_COMPONENTS + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_DIRECT_BOOT_AWARE); + if (info != null) { + mAppName = String.valueOf(mPm.getApplicationLabel(info)); + } + } catch (PackageManager.NameNotFoundException unused) { + mAppName = entry.notification.getPackageName(); + } } public String getKey() { @@ -77,13 +99,17 @@ class Bubble { return entry.notification.getPackageName(); } + public String getAppName() { + return mAppName; + } + boolean isInflated() { return mInflated; } public void updateDotVisibility() { if (iconView != null) { - iconView.updateDotVisibility(); + iconView.updateDotVisibility(true /* animate */); } } @@ -97,9 +123,9 @@ class Bubble { expandedView = (BubbleExpandedView) inflater.inflate( R.layout.bubble_expanded_view, stackView, false /* attachToRoot */); - expandedView.setEntry(entry, stackView); - + expandedView.setEntry(entry, stackView, mAppName); expandedView.setOnBlockedListener(mListener); + mInflated = true; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 48edf67a3ed4..aed117bb7871 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -193,7 +193,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (shouldCollapse) { collapseStack(); } - updateVisibility(); + updateStack(); } } @@ -534,10 +534,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } } + // Runs on state change. @Override public void apply() { mNotificationEntryManager.updateNotifications(); - updateVisibility(); + updateStack(); if (DEBUG) { Log.d(TAG, "[BubbleData]"); @@ -554,34 +555,31 @@ public class BubbleController implements ConfigurationController.ConfigurationLi }; /** - * Lets any listeners know if bubble state has changed. + * Updates the visibility of the bubbles based on current state. + * Does not un-bubble, just hides or un-hides. Notifies any + * {@link BubbleStateChangeListener}s of visibility changes. + * Updates stack description for TalkBack focus. */ - private void updateBubblesShowing() { + public void updateStack() { if (mStackView == null) { return; } + if (mStatusBarStateListener.getCurrentState() == SHADE && hasBubbles()) { + // Bubbles only appear in unlocked shade + mStackView.setVisibility(hasBubbles() ? VISIBLE : INVISIBLE); + } else if (mStackView != null) { + mStackView.setVisibility(INVISIBLE); + } + // Let listeners know if bubble state changed. boolean hadBubbles = mStatusBarWindowController.getBubblesShowing(); boolean hasBubblesShowing = hasBubbles() && mStackView.getVisibility() == VISIBLE; mStatusBarWindowController.setBubblesShowing(hasBubblesShowing); if (mStateChangeListener != null && hadBubbles != hasBubblesShowing) { mStateChangeListener.onHasBubblesChanged(hasBubblesShowing); } - } - /** - * Updates the visibility of the bubbles based on current state. - * Does not un-bubble, just hides or un-hides. Will notify any - * {@link BubbleStateChangeListener}s if visibility changes. - */ - public void updateVisibility() { - if (mStatusBarStateListener.getCurrentState() == SHADE && hasBubbles()) { - // Bubbles only appear in unlocked shade - mStackView.setVisibility(hasBubbles() ? VISIBLE : INVISIBLE); - } else if (mStackView != null) { - mStackView.setVisibility(INVISIBLE); - } - updateBubblesShowing(); + mStackView.updateContentDescription(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index 9156e06fe54e..1858244d13bc 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -178,7 +178,7 @@ public class BubbleData { Bubble bubble = getBubbleWithKey(entry.key); if (bubble == null) { // Create a new bubble - bubble = new Bubble(entry, this::onBubbleBlocked); + bubble = new Bubble(mContext, entry, this::onBubbleBlocked); doAdd(bubble); trim(); } else { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 39867c3a0bdb..fa137a1f6207 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -244,9 +244,10 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList /** * Sets the notification entry used to populate this view. */ - public void setEntry(NotificationEntry entry, BubbleStackView stackView) { + public void setEntry(NotificationEntry entry, BubbleStackView stackView, String appName) { mStackView = stackView; mEntry = entry; + mAppName = appName; ApplicationInfo info; try { @@ -257,12 +258,10 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DIRECT_BOOT_AWARE); if (info != null) { - mAppName = String.valueOf(mPm.getApplicationLabel(info)); mAppIcon = mPm.getApplicationIcon(info); } } catch (PackageManager.NameNotFoundException e) { - // Ahh... just use package name - mAppName = entry.notification.getPackageName(); + // Do nothing. } if (mAppIcon == null) { mAppIcon = mPm.getDefaultActivityIcon(); 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..4ad3a332ebe6 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -23,10 +23,9 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; +import android.app.Notification; 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 +34,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 +53,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 +67,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 +82,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 +157,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 +173,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 +183,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 +246,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 +298,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 +335,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( @@ -519,6 +554,43 @@ public class BubbleStackView extends FrameLayout { return false; } + /** + * Update content description for a11y TalkBack. + */ + public void updateContentDescription() { + if (mBubbleData.getBubbles().isEmpty()) { + return; + } + Bubble topBubble = mBubbleData.getBubbles().get(0); + String appName = topBubble.getAppName(); + Notification notification = topBubble.entry.notification.getNotification(); + CharSequence titleCharSeq = notification.extras.getCharSequence(Notification.EXTRA_TITLE); + String titleStr = getResources().getString(R.string.stream_notification); + if (titleCharSeq != null) { + titleStr = titleCharSeq.toString(); + } + int moreCount = mBubbleContainer.getChildCount() - 1; + + // Example: Title from app name. + String singleDescription = getResources().getString( + R.string.bubble_content_description_single, titleStr, appName); + + // Example: Title from app name and 4 more. + String stackDescription = getResources().getString( + R.string.bubble_content_description_stack, titleStr, appName, moreCount); + + if (mIsExpanded) { + // TODO(b/129522932) - update content description for each bubble in expanded view. + } else { + // Collapsed stack. + if (moreCount > 0) { + mBubbleContainer.setContentDescription(stackDescription); + } else { + mBubbleContainer.setContentDescription(singleDescription); + } + } + } + private void updateSystemGestureExcludeRects() { // Exclude the region occupied by the first BubbleView in the stack Rect excludeZone = mSystemGestureExclusionRects.get(0); @@ -742,7 +814,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 +1003,6 @@ public class BubbleStackView extends FrameLayout { mBubbleContainer.setController(mStackAnimationController); hideFlyoutImmediate(); - mIsDragging = true; mDraggingInDismissTarget = false; } @@ -948,20 +1019,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 +1257,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 +1309,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 +1337,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 +1350,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 +1407,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 +1429,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 +1451,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/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index acc0197c07b8..dcabb780ee3a 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -1549,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() { @@ -1815,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/globalactions/GlobalActionsGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java index e1462d15c887..03165f47c472 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java @@ -42,6 +42,8 @@ public class GlobalActionsGridLayout extends GlobalActionsLayout { listView.setReverseSublists(shouldReverseSublists()); listView.setReverseItems(shouldReverseListItems()); listView.setSwapRowsAndColumns(shouldSwapRowsAndColumns()); + + fixNavBarClipping(); } @Override @@ -73,6 +75,19 @@ public class GlobalActionsGridLayout extends GlobalActionsLayout { } } + /** + * Allows the dialog to clip over the navbar, which prevents shadows and animations from being + * cut off. + */ + private void fixNavBarClipping() { + ViewGroup parent = (ViewGroup) this.getParent(); + ViewGroup parentParent = (ViewGroup) parent.getParent(); + parent.setClipChildren(false); + parent.setClipToPadding(false); + parentParent.setClipChildren(false); + parentParent.setClipToPadding(false); + } + @Override protected ListGridLayout getListView() { return (ListGridLayout) super.getListView(); 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/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index f93c5f0827ad..a3e3426f206c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -46,6 +46,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener; import javax.inject.Inject; @@ -71,6 +72,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange private final AccessibilityController mAccessibilityController; private final DockManager mDockManager; private final Handler mMainHandler; + private final KeyguardMonitor mKeyguardMonitor; private int mLastState = 0; private boolean mTransientBiometricsError; @@ -91,7 +93,16 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange private int mIconRes; private boolean mWasPulsingOnThisFrame; private boolean mWakeAndUnlockRunning; + private boolean mKeyguardShowing; + private final KeyguardMonitor.Callback mKeyguardMonitorCallback = + new KeyguardMonitor.Callback() { + @Override + public void onKeyguardShowingChanged() { + mKeyguardShowing = mKeyguardMonitor.isShowing(); + update(false /* force */); + } + }; private final Runnable mDrawOffTimeout = () -> update(true /* forceUpdate */); private final DockManager.DockEventListener mDockEventListener = new DockManager.DockEventListener() { @@ -150,6 +161,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange StatusBarStateController statusBarStateController, ConfigurationController configurationController, AccessibilityController accessibilityController, + KeyguardMonitor keyguardMonitor, @Nullable DockManager dockManager, @Named(MAIN_HANDLER_NAME) Handler mainHandler) { super(context, attrs); @@ -159,6 +171,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange mAccessibilityController = accessibilityController; mConfigurationController = configurationController; mStatusBarStateController = statusBarStateController; + mKeyguardMonitor = keyguardMonitor; mDockManager = dockManager; mMainHandler = mainHandler; } @@ -168,6 +181,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange super.onAttachedToWindow(); mStatusBarStateController.addCallback(this); mConfigurationController.addCallback(this); + mKeyguardMonitor.addCallback(mKeyguardMonitorCallback); mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); mUnlockMethodCache.addListener(this); mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure(); @@ -183,6 +197,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange mStatusBarStateController.removeCallback(this); mConfigurationController.removeCallback(this); mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); + mKeyguardMonitor.removeCallback(mKeyguardMonitorCallback); mUnlockMethodCache.removeListener(this); if (mDockManager != null) { mDockManager.removeListener(mDockEventListener); @@ -379,7 +394,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); if (mTransientBiometricsError) { return STATE_BIOMETRICS_ERROR; - } else if (mUnlockMethodCache.canSkipBouncer() && !mSimLocked) { + } else if ((mUnlockMethodCache.canSkipBouncer() || !mKeyguardShowing) && !mSimLocked) { return STATE_LOCK_OPEN; } else if (updateMonitor.isFaceDetectionRunning()) { return STATE_SCANNING_FACE; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java index 9e0aff070364..3b3336b3f14d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java @@ -107,6 +107,8 @@ public final class NavigationBarTransitions extends BarTransitions implements @Override public void setAutoDim(boolean autoDim) { + // Ensure we aren't in gestural nav if we are triggering auto dim + if (autoDim && NavBarTintController.isEnabled(mView.getContext(), mNavBarMode)) return; if (mAutoDim == autoDim) return; mAutoDim = autoDim; applyLightsOut(true, false); 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 0b83b697e53d..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,9 @@ public class StatusBarWindowView extends FrameLayout { * Propagate {@link StatusBar} pulsing state. */ public void setPulsing(boolean pulsing) { - mLockIcon.setPulsing(pulsing); + if (mLockIcon != null) { + mLockIcon.setPulsing(pulsing); + } } /** @@ -270,14 +272,18 @@ public class StatusBarWindowView extends FrameLayout { * @param wakeAndUnlock If the type is {@link BiometricUnlockController#isWakeAndUnlock()} */ public void onBiometricAuthModeChanged(boolean wakeAndUnlock) { - mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock); + if (mLockIcon != null) { + mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock); + } } /** * Called after finished unlocking and the status bar window is already collapsed. */ public void onKeyguardFadedAway() { - mLockIcon.onKeyguardFadedAway(); + if (mLockIcon != null) { + mLockIcon.onKeyguardFadedAway(); + } } public void setStatusBarView(PhoneStatusBarView statusBarView) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java index 33b2e6e59470..364a0f7ed2e9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java @@ -103,13 +103,13 @@ public class BubbleDataTest extends SysuiTestCase { mEntryB3 = createBubbleEntry(1, "b3", "package.b"); mEntryC1 = createBubbleEntry(1, "c1", "package.c"); - mBubbleA1 = new Bubble(mEntryA1); - mBubbleA2 = new Bubble(mEntryA2); - mBubbleA3 = new Bubble(mEntryA3); - mBubbleB1 = new Bubble(mEntryB1); - mBubbleB2 = new Bubble(mEntryB2); - mBubbleB3 = new Bubble(mEntryB3); - mBubbleC1 = new Bubble(mEntryC1); + mBubbleA1 = new Bubble(mContext, mEntryA1); + mBubbleA2 = new Bubble(mContext, mEntryA2); + mBubbleA3 = new Bubble(mContext, mEntryA3); + mBubbleB1 = new Bubble(mContext, mEntryB1); + mBubbleB2 = new Bubble(mContext, mEntryB2); + mBubbleB3 = new Bubble(mContext, mEntryB3); + mBubbleC1 = new Bubble(mContext, mEntryC1); mBubbleData = new BubbleData(getContext()); @@ -803,4 +803,4 @@ public class BubbleDataTest extends SysuiTestCase { private static <T> List<T> listOf(T... a) { return ImmutableList.copyOf(a); } -} +}
\ No newline at end of file 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/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/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 5fd14a37f23c..a5a4fec59350 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -7294,6 +7294,7 @@ message MetricsEvent { ACTION_VERIFY_SLICE_OTHER_EXCEPTION = 1727; // ---- Skipping ahead to avoid conflicts between master and release branches. + // OPEN: Settings > System > Gestures > Global Actions Panel // CATEGORY: SETTINGS // OS: Q 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/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 88919df04f00..db17f8397ed4 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -475,7 +475,7 @@ public class BiometricService extends SystemService { DEFAULT_KEYGUARD_ENABLED ? 1 : 0 /* default */, userId) != 0); - if (userId == ActivityManager.getCurrentUser()) { + if (userId == ActivityManager.getCurrentUser() && !selfChange) { notifyEnabledOnKeyguardCallbacks(userId); } } else if (FACE_UNLOCK_APP_ENABLED.equals(uri)) { @@ -494,17 +494,25 @@ public class BiometricService extends SystemService { } boolean getFaceEnabledOnKeyguard() { - return mFaceEnabledOnKeyguard.getOrDefault( - ActivityManager.getCurrentUser(), DEFAULT_KEYGUARD_ENABLED); + final int user = ActivityManager.getCurrentUser(); + if (!mFaceEnabledOnKeyguard.containsKey(user)) { + onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED, user); + } + return mFaceEnabledOnKeyguard.get(user); } boolean getFaceEnabledForApps(int userId) { + if (!mFaceEnabledForApps.containsKey(userId)) { + onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED, userId); + } return mFaceEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED); } boolean getFaceAlwaysRequireConfirmation(int userId) { - return mFaceAlwaysRequireConfirmation - .getOrDefault(userId, DEFAULT_ALWAYS_REQUIRE_CONFIRMATION); + if (!mFaceAlwaysRequireConfirmation.containsKey(userId)) { + onChange(true /* selfChange */, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, userId); + } + return mFaceAlwaysRequireConfirmation.get(userId); } void notifyEnabledOnKeyguardCallbacks(int userId) { @@ -760,7 +768,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 +840,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/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/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/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java index ef94000d3a6b..388e51f203ca 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java @@ -30,6 +30,7 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; +import java.security.spec.InvalidParameterSpecException; import java.util.Arrays; import javax.crypto.BadPaddingException; @@ -43,6 +44,7 @@ import javax.crypto.spec.SecretKeySpec; public class SyntheticPasswordCrypto { private static final int PROFILE_KEY_IV_SIZE = 12; + private static final int DEFAULT_TAG_LENGTH_BITS = 128; private static final int AES_KEY_LENGTH = 32; // 256-bit AES key private static final byte[] APPLICATION_ID_PERSONALIZATION = "application-id".getBytes(); // Time between the user credential is verified with GK and the decryption of synthetic password @@ -60,13 +62,14 @@ public class SyntheticPasswordCrypto { byte[] ciphertext = Arrays.copyOfRange(blob, PROFILE_KEY_IV_SIZE, blob.length); Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE); - cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(128, iv)); + cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(DEFAULT_TAG_LENGTH_BITS, iv)); return cipher.doFinal(ciphertext); } private static byte[] encrypt(SecretKey key, byte[] blob) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, - InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + InvalidKeyException, IllegalBlockSizeException, BadPaddingException, + InvalidParameterSpecException { if (blob == null) { return null; } @@ -79,6 +82,11 @@ public class SyntheticPasswordCrypto { if (iv.length != PROFILE_KEY_IV_SIZE) { throw new RuntimeException("Invalid iv length: " + iv.length); } + final GCMParameterSpec spec = cipher.getParameters().getParameterSpec( + GCMParameterSpec.class); + if (spec.getTLen() != DEFAULT_TAG_LENGTH_BITS) { + throw new RuntimeException("Invalid tag length: " + spec.getTLen()); + } ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); outputStream.write(iv); outputStream.write(ciphertext); @@ -92,7 +100,8 @@ public class SyntheticPasswordCrypto { try { return encrypt(key, message); } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException - | IllegalBlockSizeException | BadPaddingException | IOException e) { + | IllegalBlockSizeException | BadPaddingException | IOException + | InvalidParameterSpecException e) { e.printStackTrace(); return null; } @@ -147,7 +156,7 @@ public class SyntheticPasswordCrypto { public static byte[] createBlob(String keyAlias, byte[] data, byte[] applicationId, long sid) { try { KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES); - keyGenerator.init(new SecureRandom()); + keyGenerator.init(AES_KEY_LENGTH * 8, new SecureRandom()); SecretKey secretKey = keyGenerator.generateKey(); KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore.load(null); @@ -169,7 +178,8 @@ public class SyntheticPasswordCrypto { } catch (CertificateException | IOException | BadPaddingException | IllegalBlockSizeException | KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException - | InvalidKeyException e) { + | InvalidKeyException + | InvalidParameterSpecException e) { e.printStackTrace(); throw new RuntimeException("Failed to encrypt blob", e); } 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/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/updates/ConversationActionsInstallReceiver.java b/services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java index 7310af3a6eb6..bbe534e5aeda 100644 --- a/services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java +++ b/services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java @@ -25,4 +25,9 @@ public class ConversationActionsInstallReceiver extends ConfigUpdateInstallRecei "metadata/actions_suggestions", "version"); } + + @Override + protected boolean verifyVersion(int current, int alternative) { + return true; + } } diff --git a/services/core/java/com/android/server/updates/LangIdInstallReceiver.java b/services/core/java/com/android/server/updates/LangIdInstallReceiver.java index 05dad2178798..ecfa3aa97ac0 100644 --- a/services/core/java/com/android/server/updates/LangIdInstallReceiver.java +++ b/services/core/java/com/android/server/updates/LangIdInstallReceiver.java @@ -25,4 +25,9 @@ public class LangIdInstallReceiver extends ConfigUpdateInstallReceiver { "metadata/lang_id", "version"); } + + @Override + protected boolean verifyVersion(int current, int alternative) { + return true; + } } 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 fea0793726c2..9d08e10c6dea 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; @@ -297,7 +296,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/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index ef2a21d919e1..b30da5e156e2 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -819,7 +819,7 @@ public class LockTaskController { } catch (Settings.SettingNotFoundException e) { // Log to SafetyNet for b/127605586 android.util.EventLog.writeEvent(0x534e4554, "127605586", -1, ""); - return mLockPatternUtils.isSecure(USER_CURRENT); + return getLockPatternUtils().isSecure(USER_CURRENT); } } 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/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java index fb34913a5606..1ab3b98ae78d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java @@ -349,7 +349,7 @@ class SecurityLogMonitor implements Runnable { lastPos++; } else { // Two events have the same timestamp, check if they are the same. - if (lastEvent.equals(curEvent)) { + if (lastEvent.eventEquals(curEvent)) { // Actual overlap, just skip the event. if (DEBUG) Slog.d(TAG, "Skipped dup event with timestamp: " + lastNanos); } else { 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/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java index 09e20e04835b..1f5ebe4536d8 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java @@ -235,6 +235,10 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase { mPasswordSlotManager.cleanup(); } + protected void flushHandlerTasks() { + mService.mHandler.runWithScissors(() -> { }, 0 /*now*/); // Flush runnables on handler + } + protected void assertNotEquals(long expected, long actual) { assertTrue(expected != actual); } 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 10fb3ba938d4..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; } @@ -163,5 +163,4 @@ public class LockSettingsServiceTestable extends LockSettingsService { } return storedData; } - } 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/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java index 0273f7651207..1cd590c39f49 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java @@ -232,7 +232,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { reset(mAuthSecretService); mService.onUnlockUser(PRIMARY_USER_ID); - mService.mHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush runnables on handler + flushHandlerTasks(); verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class)); } @@ -242,7 +242,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { reset(mAuthSecretService); mService.onUnlockUser(PRIMARY_USER_ID); - mService.mHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush runnables on handler + flushHandlerTasks(); verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class)); } @@ -254,7 +254,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { reset(mAuthSecretService); mService.onUnlockUser(PRIMARY_USER_ID); - mService.mHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush runnables on handler + flushHandlerTasks(); verify(mAuthSecretService).primaryUserCredential(any(ArrayList.class)); } @@ -357,7 +357,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { handle, token, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID); // Verify DPM gets notified about new device lock - mService.mHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush runnables on handler + flushHandlerTasks(); final PasswordMetrics metric = PasswordMetrics.computeForCredential( LockPatternUtils.CREDENTIAL_TYPE_PATTERN, pattern); verify(mDevicePolicyManager).setActivePasswordState(metric, PRIMARY_USER_ID); @@ -384,6 +384,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { mLocalService.setLockCredentialWithToken(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, handle, token, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID); + flushHandlerTasks(); // flush the unlockUser() call before changing password again mLocalService.setLockCredentialWithToken(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, handle, token, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index fe4541183038..7f35dac9611a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -379,6 +379,7 @@ public class AppWindowTokenTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131176283) public void testCreateRemoveStartingWindow() { mToken.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, 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..be4729a679b9 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -1249,6 +1249,7 @@ public class TelecomManager { * @deprecated Use * {@link android.app.role.RoleManager#addRoleHolderAsUser(String, String, int, UserHandle, * Executor, java.util.function.Consumer)} instead. + * @removed */ @SystemApi @Deprecated @@ -1451,6 +1452,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 +1462,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/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/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java index 64c1830c5e5c..2552f045eedf 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java @@ -209,18 +209,9 @@ public final class TelephonyPermissions { context.enforcePermission( android.Manifest.permission.READ_PHONE_STATE, pid, uid, message); } catch (SecurityException phoneStateException) { - SubscriptionManager sm = (SubscriptionManager) context.getSystemService( - Context.TELEPHONY_SUBSCRIPTION_SERVICE); - int[] activeSubIds = sm.getActiveSubscriptionIdList(); - for (int activeSubId : activeSubIds) { - // If we don't have the runtime permission, but do have carrier privileges, that - // suffices for reading phone state. - if (getCarrierPrivilegeStatus(telephonySupplier, activeSubId, uid) - == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { - return true; - } - } - return false; + // If we don't have the runtime permission, but do have carrier privileges, that + // suffices for reading phone state. + return checkCarrierPrivilegeForAnySubId(context, telephonySupplier, uid); } } @@ -236,9 +227,9 @@ public final class TelephonyPermissions { * * <p>This method behaves in one of the following ways: * <ul> - * <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission or the - * calling package passes a DevicePolicyManager Device Owner / Profile Owner device - * identifier access check, + * <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the calling + * package passes a DevicePolicyManager Device Owner / Profile Owner device identifier + * access check, or the calling package has carrier privileges. * <li>throw SecurityException: if the caller does not meet any of the requirements and is * targeting Q or is targeting pre-Q and does not have the READ_PHONE_STATE permission. * <li>return false: if the caller is targeting pre-Q and does have the READ_PHONE_STATE @@ -258,9 +249,9 @@ public final class TelephonyPermissions { * * <p>This method behaves in one of the following ways: * <ul> - * <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission or the - * calling package passes a DevicePolicyManager Device Owner / Profile Owner device - * identifier access check, + * <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the calling + * package passes a DevicePolicyManager Device Owner / Profile Owner device identifier + * access check, or the calling package has carrier privileges. * <li>throw SecurityException: if the caller does not meet any of the requirements and is * targeting Q or is targeting pre-Q and does not have the READ_PHONE_STATE permission * or carrier privileges. @@ -272,23 +263,8 @@ public final class TelephonyPermissions { */ public static boolean checkCallingOrSelfReadDeviceIdentifiers(Context context, int subId, String callingPackage, String message) { - int pid = Binder.getCallingPid(); - int uid = Binder.getCallingUid(); - // if the device identifier check completes successfully then grant access. - if (checkReadDeviceIdentifiers(context, pid, uid, callingPackage)) { - return true; - } - // Calling packages with carrier privileges will also have access to device identifiers, but - // this may be removed in a future release. - if (checkCarrierPrivilegeForSubId(subId)) { - return true; - } - // else the calling package is not authorized to access the device identifiers; call - // a central method to report the failure based on the target SDK and if the calling package - // has the READ_PHONE_STATE permission or carrier privileges that were previously required - // to access the identifiers. - return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage, - message); + return checkReadDeviceIdentifiers(context, TELEPHONY_SUPPLIER, subId, + Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, message); } /** @@ -298,7 +274,7 @@ public final class TelephonyPermissions { * <ul> * <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the calling * package passes a DevicePolicyManager Device Owner / Profile Owner device identifier - * access check, or the calling package has carrier privleges. + * access check, or the calling package has carrier privileges. * <li>throw SecurityException: if the caller does not meet any of the requirements and is * targeting Q or is targeting pre-Q and does not have the READ_PHONE_STATE permission. * <li>return false: if the caller is targeting pre-Q and does have the READ_PHONE_STATE @@ -309,19 +285,8 @@ public final class TelephonyPermissions { */ public static boolean checkCallingOrSelfReadSubscriberIdentifiers(Context context, int subId, String callingPackage, String message) { - int pid = Binder.getCallingPid(); - int uid = Binder.getCallingUid(); - // if the device identifiers can be read then grant access to the subscriber identifiers - if (checkReadDeviceIdentifiers(context, pid, uid, callingPackage)) { - return true; - } - // If the calling package has carrier privileges then allow access to the subscriber - // identifiers. - if (checkCarrierPrivilegeForSubId(subId)) { - return true; - } - return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage, - message); + return checkReadDeviceIdentifiers(context, TELEPHONY_SUPPLIER, subId, + Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, message); } /** @@ -331,8 +296,10 @@ public final class TelephonyPermissions { * package passes a DevicePolicyManager Device Owner / Profile Owner device identifier access * check. */ - private static boolean checkReadDeviceIdentifiers(Context context, int pid, int uid, - String callingPackage) { + @VisibleForTesting + public static boolean checkReadDeviceIdentifiers(Context context, + Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid, + String callingPackage, String message) { // Allow system and root access to the device identifiers. final int appId = UserHandle.getAppId(uid); if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID) { @@ -343,31 +310,36 @@ public final class TelephonyPermissions { uid) == PackageManager.PERMISSION_GRANTED) { return true; } - // if the calling package is null then return now as there's no way to perform the - // DevicePolicyManager device / profile owner and AppOp checks - if (callingPackage == null) { - return false; + // If the calling package has carrier privileges for any subscription then allow access. + if (checkCarrierPrivilegeForAnySubId(context, telephonySupplier, uid)) { + return true; } - // Allow access to an app that has been granted the READ_DEVICE_IDENTIFIERS app op. - long token = Binder.clearCallingIdentity(); - AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService( - Context.APP_OPS_SERVICE); - try { - if (appOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_READ_DEVICE_IDENTIFIERS, uid, - callingPackage) == AppOpsManager.MODE_ALLOWED) { + // if the calling package is not null then perform the DevicePolicyManager device / + // profile owner and Appop checks. + if (callingPackage != null) { + // Allow access to an app that has been granted the READ_DEVICE_IDENTIFIERS app op. + long token = Binder.clearCallingIdentity(); + AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService( + Context.APP_OPS_SERVICE); + try { + if (appOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_READ_DEVICE_IDENTIFIERS, uid, + callingPackage) == AppOpsManager.MODE_ALLOWED) { + return true; + } + } finally { + Binder.restoreCallingIdentity(token); + } + // Allow access to a device / profile owner app. + DevicePolicyManager devicePolicyManager = + (DevicePolicyManager) context.getSystemService( + Context.DEVICE_POLICY_SERVICE); + if (devicePolicyManager != null && devicePolicyManager.checkDeviceIdentifierAccess( + callingPackage, pid, uid)) { return true; } - } finally { - Binder.restoreCallingIdentity(token); } - // Allow access to a device / profile owner app. - DevicePolicyManager devicePolicyManager = (DevicePolicyManager) context.getSystemService( - Context.DEVICE_POLICY_SERVICE); - if (devicePolicyManager != null && devicePolicyManager.checkDeviceIdentifierAccess( - callingPackage, pid, uid)) { - return true; - } - return false; + return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage, + message); } /** @@ -403,10 +375,12 @@ public final class TelephonyPermissions { try { callingPackageInfo = context.getPackageManager().getApplicationInfoAsUser( callingPackage, 0, UserHandle.getUserId(uid)); - if (callingPackageInfo.isSystemApp()) { - isPreinstalled = true; - if (callingPackageInfo.isPrivilegedApp()) { - isPrivApp = true; + if (callingPackageInfo != null) { + if (callingPackageInfo.isSystemApp()) { + isPreinstalled = true; + if (callingPackageInfo.isPrivilegedApp()) { + isPrivApp = true; + } } } } catch (PackageManager.NameNotFoundException e) { @@ -651,6 +625,26 @@ public final class TelephonyPermissions { } } + /** + * Returns whether the provided uid has carrier privileges for any active subscription ID. + */ + private static boolean checkCarrierPrivilegeForAnySubId(Context context, + Supplier<ITelephony> telephonySupplier, int uid) { + SubscriptionManager sm = (SubscriptionManager) context.getSystemService( + Context.TELEPHONY_SUBSCRIPTION_SERVICE); + int[] activeSubIds = sm.getActiveSubscriptionIdList(); + if (activeSubIds != null) { + for (int activeSubId : activeSubIds) { + if (getCarrierPrivilegeStatus(telephonySupplier, activeSubId, uid) + == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { + return true; + } + } + } + return false; + } + + private static int getCarrierPrivilegeStatus( Supplier<ITelephony> telephonySupplier, int subId, int uid) { ITelephony telephony = telephonySupplier.get(); diff --git a/tests/net/java/android/net/IpMemoryStoreTest.java b/tests/net/java/android/net/IpMemoryStoreTest.java index 8ff2de9777c9..6e69b34fe474 100644 --- a/tests/net/java/android/net/IpMemoryStoreTest.java +++ b/tests/net/java/android/net/IpMemoryStoreTest.java @@ -111,13 +111,12 @@ public class IpMemoryStoreTest { @Test public void testNetworkAttributes() throws Exception { - startIpMemoryStore(true); + startIpMemoryStore(true /* supplyService */); final String l2Key = "fakeKey"; mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, - status -> { - assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); - }); + status -> assertTrue("Store not successful : " + status.resultCode, + status.isSuccess())); verify(mMockService, times(1)).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any()); assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); @@ -135,7 +134,7 @@ public class IpMemoryStoreTest { @Test public void testPrivateData() throws RemoteException { - startIpMemoryStore(true); + startIpMemoryStore(true /* supplyService */); final Blob b = new Blob(); b.data = TEST_BLOB_DATA; final String l2Key = "fakeKey"; @@ -162,7 +161,7 @@ public class IpMemoryStoreTest { @Test public void testFindL2Key() throws UnknownHostException, RemoteException, Exception { - startIpMemoryStore(true); + startIpMemoryStore(true /* supplyService */); final String l2Key = "fakeKey"; mStore.findL2Key(TEST_NETWORK_ATTRIBUTES, @@ -177,7 +176,7 @@ public class IpMemoryStoreTest { @Test public void testIsSameNetwork() throws UnknownHostException, RemoteException { - startIpMemoryStore(true); + startIpMemoryStore(true /* supplyService */); final String l2Key1 = "fakeKey1"; final String l2Key2 = "fakeKey2"; @@ -193,7 +192,7 @@ public class IpMemoryStoreTest { @Test public void testEnqueuedIpMsRequests() throws Exception { - startIpMemoryStore(false); + startIpMemoryStore(false /* supplyService */); final Blob b = new Blob(); b.data = TEST_BLOB_DATA; @@ -201,9 +200,8 @@ public class IpMemoryStoreTest { // enqueue multiple ipms requests mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, - status -> { - assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); - }); + status -> assertTrue("Store not successful : " + status.resultCode, + status.isSuccess())); mStore.retrieveNetworkAttributes(l2Key, (status, key, attr) -> { assertTrue("Retrieve network attributes not successful : " @@ -212,9 +210,8 @@ public class IpMemoryStoreTest { assertEquals(TEST_NETWORK_ATTRIBUTES, attr); }); mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, - status -> { - assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); - }); + status -> assertTrue("Store not successful : " + status.resultCode, + status.isSuccess())); mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, (status, key, name, data) -> { assertTrue("Retrieve blob status not successful : " + status.resultCode, @@ -240,7 +237,7 @@ public class IpMemoryStoreTest { @Test public void testEnqueuedIpMsRequestsWithException() throws Exception { - startIpMemoryStore(true); + startIpMemoryStore(true /* supplyService */); doThrow(RemoteException.class).when(mMockService).retrieveNetworkAttributes(any(), any()); final Blob b = new Blob(); @@ -249,9 +246,8 @@ public class IpMemoryStoreTest { // enqueue multiple ipms requests mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, - status -> { - assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); - }); + status -> assertTrue("Store not successful : " + status.resultCode, + status.isSuccess())); mStore.retrieveNetworkAttributes(l2Key, (status, key, attr) -> { assertTrue("Retrieve network attributes not successful : " @@ -260,9 +256,8 @@ public class IpMemoryStoreTest { assertEquals(TEST_NETWORK_ATTRIBUTES, attr); }); mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, - status -> { - assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); - }); + status -> assertTrue("Store not successful : " + status.resultCode, + status.isSuccess())); mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, (status, key, name, data) -> { assertTrue("Retrieve blob status not successful : " + status.resultCode, @@ -286,7 +281,7 @@ public class IpMemoryStoreTest { @Test public void testEnqueuedIpMsRequestsCallbackFunctionWithException() throws Exception { - startIpMemoryStore(true); + startIpMemoryStore(true /* supplyService */); final Blob b = new Blob(); b.data = TEST_BLOB_DATA; @@ -294,9 +289,8 @@ public class IpMemoryStoreTest { // enqueue multiple ipms requests mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, - status -> { - assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); - }); + status -> assertTrue("Store not successful : " + status.resultCode, + status.isSuccess())); mStore.retrieveNetworkAttributes(l2Key, (status, key, attr) -> { throw new RuntimeException("retrieveNetworkAttributes test"); @@ -320,6 +314,7 @@ public class IpMemoryStoreTest { inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any()); + inOrder.verify(mMockService).retrieveNetworkAttributes(eq(l2Key), any()); inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME), eq(b), any()); inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID), 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/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h index c1fcf5cad240..27ffcdf52168 100644 --- a/tools/aapt/SdkConstants.h +++ b/tools/aapt/SdkConstants.h @@ -44,6 +44,7 @@ enum { SDK_O = 26, SDK_O_MR1 = 27, SDK_P = 28, + SDK_Q = 29, }; #endif // H_AAPT_SDK_CONSTANTS diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h index 9fa29f25c29c..580daabbd577 100644 --- a/tools/aapt2/SdkConstants.h +++ b/tools/aapt2/SdkConstants.h @@ -54,6 +54,7 @@ enum : ApiVersion { SDK_O = 26, SDK_O_MR1 = 27, SDK_P = 28, + SDK_Q = 29, }; ApiVersion FindAttributeSdkLevel(const ResourceId& id); diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py index f191f6c0ab78..6683e2abb0da 100755 --- a/tools/fonts/fontchain_linter.py +++ b/tools/fonts/fontchain_linter.py @@ -490,11 +490,6 @@ def remove_emoji_exclude(source, items): def flag_sequence(territory_code): return tuple(0x1F1E6 + ord(ch) - ord('A') for ch in territory_code) -UNSUPPORTED_FLAGS = frozenset({ - flag_sequence('BL'), flag_sequence('BQ'), flag_sequence('MQ'), - flag_sequence('RE'), flag_sequence('TF'), -}) - EQUIVALENT_FLAGS = { flag_sequence('BV'): flag_sequence('NO'), flag_sequence('CP'): flag_sequence('FR'), @@ -600,9 +595,6 @@ def compute_expected_emoji(): for first, second in SAME_FLAG_MAPPINGS: equivalent_emoji[first] = second - # Remove unsupported flags - all_sequences.difference_update(UNSUPPORTED_FLAGS) - # Add all tag characters used in flags sequence_pieces.update(range(0xE0030, 0xE0039 + 1)) sequence_pieces.update(range(0xE0061, 0xE007A + 1)) 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; |