diff options
138 files changed, 3095 insertions, 1232 deletions
diff --git a/api/current.txt b/api/current.txt index 54fb459b3bc8..3ec7f447e8b9 100644 --- a/api/current.txt +++ b/api/current.txt @@ -220,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 @@ -284,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 @@ -572,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 @@ -646,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 @@ -707,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 @@ -735,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 @@ -767,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 @@ -944,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 @@ -1125,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 @@ -1204,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 @@ -1213,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 @@ -1332,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 @@ -1513,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 @@ -1627,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 { @@ -34166,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 { @@ -56251,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(); @@ -56265,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); @@ -56278,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..d69eced236c8 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -299,6 +299,10 @@ message Atom { CarPowerStateChanged car_power_state_changed = 203; GarageModeInfo garage_mode_info = 204; TestAtomReported test_atom_reported = 205 [(log_from_module) = "cts"]; + ContentCaptureCallerMismatchReported content_capture_caller_mismatch_reported = 206; + ContentCaptureServiceEvents content_capture_service_events = 207; + ContentCaptureSessionEvents content_capture_session_events = 208; + ContentCaptureFlushed content_capture_flushed = 209; } // Pulled events will start at field 10000. @@ -364,6 +368,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. @@ -4830,6 +4835,95 @@ message BuildInformation { } /** + * Logs information about mismatched caller for content capture. + * + * Logged from: + * frameworks/base/core/java/android/service/contentcapture/ContentCaptureService.java + */ +message ContentCaptureCallerMismatchReported { + optional string intended_package = 1; + optional string calling_package = 2; +} + +/** + * Logs information about content capture service events. + * + * Logged from: + * frameworks/base/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java + */ +message ContentCaptureServiceEvents { + // The type of event. + enum Event { + UNKNOWN = 0; + ON_CONNECTED = 1; + ON_DISCONNECTED = 2; + SET_WHITELIST = 3; + SET_DISABLED = 4; + ON_USER_DATA_REMOVED = 5; + } + optional Event event = 1; + // component/package of content capture service. + optional string service_info = 2; + // component/package of target. + // it's a concatenated list of component/package for SET_WHITELIST event + // separated by " ". + optional string target_info = 3; +} + +/** + * Logs information about content capture session events. + * + * Logged from: + * frameworks/base/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java + */ +message ContentCaptureSessionEvents { + // The type of event. + enum Event { + UNKNOWN = 0; + ON_SESSION_STARTED = 1; + ON_SESSION_FINISHED = 2; + SESSION_NOT_CREATED = 3; + } + optional int32 session_id = 1; + optional Event event = 2; + // (n/a on session finished) + optional int32 state_flags = 3; + // component/package of content capture service. + optional string service_info = 4; + // component/package of app. + // (n/a on session finished) + optional string app_info = 5; + optional bool is_child_session = 6; +} + +/** + * Logs information about session being flushed. + * + * Logged from: + * frameworks/base/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java + */ +message ContentCaptureFlushed { + optional int32 session_id = 1; + // component/package of content capture service. + optional string service_info = 2; + // component/package of app. + optional string app_info = 3; + // session start/finish events + optional int32 child_session_started = 4; + optional int32 child_session_finished = 5; + // count of view events. + optional int32 view_appeared_count = 6; + optional int32 view_disappeared_count = 7; + optional int32 view_text_changed_count = 8; + + // Flush stats. + optional int32 max_events = 9; + optional int32 idle_flush_freq = 10; + optional int32 text_flush_freq = 11; + optional int32 flush_reason = 12; +} + +/** * Pulls on-device BatteryStats power use calculations for the overall device. */ message DeviceCalculatedPowerUse { @@ -6357,3 +6451,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/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 4e419b6acddc..a2fd9d42f488 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -330,6 +330,8 @@ message IncidentdDetails { // Class name of the incident report receiver. optional string receiver_cls = 4; + + optional string alert_description = 5; } message PerfettoDetails { diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp index ff1cb4ff1450..f2c6f1ad6759 100644 --- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp +++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp @@ -36,12 +36,14 @@ namespace statsd { using android::util::ProtoOutputStream; using std::vector; -using util::FIELD_TYPE_MESSAGE; using util::FIELD_TYPE_INT32; using util::FIELD_TYPE_INT64; +using util::FIELD_TYPE_MESSAGE; +using util::FIELD_TYPE_STRING; // field ids in IncidentHeaderProto const int FIELD_ID_ALERT_ID = 1; +const int FIELD_ID_REASON = 2; const int FIELD_ID_CONFIG_KEY = 3; const int FIELD_ID_CONFIG_KEY_UID = 1; const int FIELD_ID_CONFIG_KEY_ID = 2; @@ -57,9 +59,11 @@ const int FIELD_ID_PACKAGE_INFO = 3; namespace { void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensionKey& dimensionKey, - int64_t metricValue, const ConfigKey& configKey, vector<uint8_t>* protoData) { + int64_t metricValue, const ConfigKey& configKey, const string& reason, + vector<uint8_t>* protoData) { ProtoOutputStream headerProto; headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_ID, (long long)rule_id); + headerProto.write(FIELD_TYPE_STRING | FIELD_ID_REASON, reason); uint64_t token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY); headerProto.write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_KEY_UID, configKey.GetUid()); @@ -142,7 +146,8 @@ bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int IncidentReportArgs incidentReport; vector<uint8_t> protoData; - getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey, &protoData); + getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey, + config.alert_description(), &protoData); incidentReport.addHeader(protoData); for (int i = 0; i < config.section_size(); i++) { 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/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/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/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/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/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index b0142ea981de..f662b616cf86 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -98,7 +98,7 @@ public final class CameraManager { * identifiers, while removable cameras have a unique identifier for each * individual device, even if they are the same model.</p> * - * <p>This list doesn't contain physical cameras that can only used as part of a logical + * <p>This list doesn't contain physical cameras that can only be used as part of a logical * multi-camera device.</p> * * @return The list of currently connected camera devices. @@ -263,7 +263,7 @@ public final class CameraManager { * immutable for a given camera.</p> * * <p>From API level 29, this function can also be used to query the capabilities of physical - * cameras that can only be used as part of logical multi-camera. These cameras cannot not be + * cameras that can only be used as part of logical multi-camera. These cameras cannot be * opened directly via {@link #openCamera}</p> * * @param cameraId The id of the camera device to query. This could be either a standalone 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/IncidentManager.java b/core/java/android/os/IncidentManager.java index ed8d3f7cb075..a94fd65943a9 100644 --- a/core/java/android/os/IncidentManager.java +++ b/core/java/android/os/IncidentManager.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -34,6 +35,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; /** * Class to take an incident report. @@ -248,6 +250,24 @@ public class IncidentManager { public @NonNull String toString() { return "PendingReport(" + getUri().toString() + ")"; } + + /** + * @inheritDoc + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof PendingReport)) { + return false; + } + final PendingReport that = (PendingReport) obj; + return this.mUri.equals(that.mUri) + && this.mFlags == that.mFlags + && this.mRequestingPackage.equals(that.mRequestingPackage) + && this.mTimestamp == that.mTimestamp; + } } /** @@ -355,21 +375,35 @@ public class IncidentManager { } /** - * Listener for the status of an incident report being authroized or denied. + * Listener for the status of an incident report being authorized or denied. * * @see #requestAuthorization * @see #cancelAuthorization */ public static class AuthListener { + Executor mExecutor; + IIncidentAuthListener.Stub mBinder = new IIncidentAuthListener.Stub() { @Override public void onReportApproved() { - AuthListener.this.onReportApproved(); + if (mExecutor != null) { + mExecutor.execute(() -> { + AuthListener.this.onReportApproved(); + }); + } else { + AuthListener.this.onReportApproved(); + } } @Override public void onReportDenied() { - AuthListener.this.onReportDenied(); + if (mExecutor != null) { + mExecutor.execute(() -> { + AuthListener.this.onReportDenied(); + }); + } else { + AuthListener.this.onReportDenied(); + } } }; @@ -410,7 +444,23 @@ public class IncidentManager { @RequiresPermission(android.Manifest.permission.REQUEST_INCIDENT_REPORT_APPROVAL) public void requestAuthorization(int callingUid, String callingPackage, int flags, AuthListener listener) { + requestAuthorization(callingUid, callingPackage, flags, + mContext.getMainExecutor(), listener); + } + + /** + * Request authorization of an incident report. + * @hide + */ + @RequiresPermission(android.Manifest.permission.REQUEST_INCIDENT_REPORT_APPROVAL) + public void requestAuthorization(int callingUid, @NonNull String callingPackage, int flags, + @NonNull @CallbackExecutor Executor executor, @NonNull AuthListener listener) { try { + if (listener.mExecutor != null) { + throw new RuntimeException("Do not reuse AuthListener objects when calling" + + " requestAuthorization"); + } + listener.mExecutor = executor; getCompanionServiceLocked().authorizeReport(callingUid, callingPackage, null, null, flags, listener.mBinder); } catch (RemoteException ex) { 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/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index 02ce87324a4f..08d9733ac815 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -29,6 +29,7 @@ import android.annotation.SystemApi; import android.annotation.TestApi; import android.app.Service; import android.content.ComponentName; +import android.content.ContentCaptureOptions; import android.content.Intent; import android.content.pm.ParceledListSlice; import android.os.Binder; @@ -40,6 +41,7 @@ import android.os.RemoteException; import android.util.Log; import android.util.Slog; import android.util.SparseIntArray; +import android.util.StatsLog; import android.view.contentcapture.ContentCaptureCondition; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureEvent; @@ -114,6 +116,9 @@ public abstract class ContentCaptureService extends Service { private Handler mHandler; private IContentCaptureServiceCallback mCallback; + private long mCallerMismatchTimeout = 1000; + private long mLastCallerMismatchLog; + /** * Binder that receives calls from the system server. */ @@ -176,9 +181,10 @@ public abstract class ContentCaptureService extends Service { new IContentCaptureDirectManager.Stub() { @Override - public void sendEvents(@SuppressWarnings("rawtypes") ParceledListSlice events) { + public void sendEvents(@SuppressWarnings("rawtypes") ParceledListSlice events, int reason, + ContentCaptureOptions options) { mHandler.sendMessage(obtainMessage(ContentCaptureService::handleSendEvents, - ContentCaptureService.this, Binder.getCallingUid(), events)); + ContentCaptureService.this, Binder.getCallingUid(), events, reason, options)); } }; @@ -424,14 +430,23 @@ public abstract class ContentCaptureService extends Service { } private void handleSendEvents(int uid, - @NonNull ParceledListSlice<ContentCaptureEvent> parceledEvents) { + @NonNull ParceledListSlice<ContentCaptureEvent> parceledEvents, int reason, + @Nullable ContentCaptureOptions options) { + final List<ContentCaptureEvent> events = parceledEvents.getList(); + if (events.isEmpty()) { + Log.w(TAG, "handleSendEvents() received empty list of events"); + return; + } + + // Metrics. + final FlushMetrics metrics = new FlushMetrics(); + ComponentName activityComponent = null; // Most events belong to the same session, so we can keep a reference to the last one // to avoid creating too many ContentCaptureSessionId objects int lastSessionId = NO_SESSION_ID; ContentCaptureSessionId sessionId = null; - final List<ContentCaptureEvent> events = parceledEvents.getList(); for (int i = 0; i < events.size(); i++) { final ContentCaptureEvent event = events.get(i); if (!handleIsRightCallerFor(event, uid)) continue; @@ -439,22 +454,44 @@ public abstract class ContentCaptureService extends Service { if (sessionIdInt != lastSessionId) { sessionId = new ContentCaptureSessionId(sessionIdInt); lastSessionId = sessionIdInt; + if (i != 0) { + writeFlushMetrics(lastSessionId, activityComponent, metrics, options, reason); + metrics.reset(); + } + } + final ContentCaptureContext clientContext = event.getContentCaptureContext(); + if (activityComponent == null && clientContext != null) { + activityComponent = clientContext.getActivityComponent(); } switch (event.getType()) { case ContentCaptureEvent.TYPE_SESSION_STARTED: - final ContentCaptureContext clientContext = event.getContentCaptureContext(); clientContext.setParentSessionId(event.getParentSessionId()); mSessionUids.put(sessionIdInt, uid); onCreateContentCaptureSession(clientContext, sessionId); + metrics.sessionStarted++; break; case ContentCaptureEvent.TYPE_SESSION_FINISHED: mSessionUids.delete(sessionIdInt); onDestroyContentCaptureSession(sessionId); + metrics.sessionFinished++; + break; + case ContentCaptureEvent.TYPE_VIEW_APPEARED: + onContentCaptureEvent(sessionId, event); + metrics.viewAppearedCount++; + break; + case ContentCaptureEvent.TYPE_VIEW_DISAPPEARED: + onContentCaptureEvent(sessionId, event); + metrics.viewDisappearedCount++; + break; + case ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED: + onContentCaptureEvent(sessionId, event); + metrics.viewTextChangedCount++; break; default: onContentCaptureEvent(sessionId, event); } } + writeFlushMetrics(lastSessionId, activityComponent, metrics, options, reason); } private void handleOnActivitySnapshot(int sessionId, @NonNull SnapshotData snapshotData) { @@ -499,7 +536,13 @@ public abstract class ContentCaptureService extends Service { if (rightUid != uid) { Log.e(TAG, "invalid call from UID " + uid + ": session " + sessionId + " belongs to " + rightUid); - //TODO(b/111276913): log metrics as this could be a malicious app forging a sessionId + long now = System.currentTimeMillis(); + if (now - mLastCallerMismatchLog > mCallerMismatchTimeout) { + StatsLog.write(StatsLog.CONTENT_CAPTURE_CALLER_MISMATCH_REPORTED, + getPackageManager().getNameForUid(rightUid), + getPackageManager().getNameForUid(uid)); + mLastCallerMismatchLog = now; + } return false; } return true; @@ -530,4 +573,22 @@ public abstract class ContentCaptureService extends Service { Slog.w(TAG, "Error async reporting result to client: " + e); } } + + /** + * Logs the metrics for content capture events flushing. + */ + private void writeFlushMetrics(int sessionId, @Nullable ComponentName app, + @NonNull FlushMetrics flushMetrics, @Nullable ContentCaptureOptions options, + int flushReason) { + if (mCallback == null) { + Log.w(TAG, "writeSessionFlush(): no server callback"); + return; + } + + try { + mCallback.writeSessionFlush(sessionId, app, flushMetrics, options, flushReason); + } catch (RemoteException e) { + Log.e(TAG, "failed to write flush metrics: " + e); + } + } } diff --git a/core/java/android/service/contentcapture/FlushMetrics.aidl b/core/java/android/service/contentcapture/FlushMetrics.aidl new file mode 100644 index 000000000000..d0b935f3c4cb --- /dev/null +++ b/core/java/android/service/contentcapture/FlushMetrics.aidl @@ -0,0 +1,20 @@ +/** + * 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 android.service.contentcapture; + +/* @hide */ +parcelable FlushMetrics; diff --git a/core/java/android/service/contentcapture/FlushMetrics.java b/core/java/android/service/contentcapture/FlushMetrics.java new file mode 100644 index 000000000000..01f3a12ecea9 --- /dev/null +++ b/core/java/android/service/contentcapture/FlushMetrics.java @@ -0,0 +1,79 @@ +/* + * 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 android.service.contentcapture; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Holds metrics for content capture events flushing. + * + * @hide + */ +public final class FlushMetrics implements Parcelable { + public int viewAppearedCount; + public int viewDisappearedCount; + public int viewTextChangedCount; + public int sessionStarted; + public int sessionFinished; + + /** + * Resets all flush metrics. + */ + public void reset() { + viewAppearedCount = 0; + viewDisappearedCount = 0; + viewTextChangedCount = 0; + sessionStarted = 0; + sessionFinished = 0; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(sessionStarted); + out.writeInt(sessionFinished); + out.writeInt(viewAppearedCount); + out.writeInt(viewDisappearedCount); + out.writeInt(viewTextChangedCount); + } + + @NonNull + public static final Creator<FlushMetrics> CREATOR = new Creator<FlushMetrics>() { + @NonNull + @Override + public FlushMetrics createFromParcel(Parcel in) { + final FlushMetrics flushMetrics = new FlushMetrics(); + flushMetrics.sessionStarted = in.readInt(); + flushMetrics.sessionFinished = in.readInt(); + flushMetrics.viewAppearedCount = in.readInt(); + flushMetrics.viewDisappearedCount = in.readInt(); + flushMetrics.viewTextChangedCount = in.readInt(); + return flushMetrics; + } + + @Override + public FlushMetrics[] newArray(int size) { + return new FlushMetrics[size]; + } + }; +} diff --git a/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl index 0550ad3ea20c..ea6e76b47853 100644 --- a/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl +++ b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl @@ -18,6 +18,8 @@ package android.service.contentcapture; import android.content.ComponentName; import android.view.contentcapture.ContentCaptureCondition; +import android.service.contentcapture.FlushMetrics; +import android.content.ContentCaptureOptions; import java.util.List; @@ -30,4 +32,8 @@ oneway interface IContentCaptureServiceCallback { void setContentCaptureWhitelist(in List<String> packages, in List<ComponentName> activities); void setContentCaptureConditions(String packageName, in List<ContentCaptureCondition> conditions); void disableSelf(); - } + + // Logs aggregated content capture flush metrics to Statsd + void writeSessionFlush(int sessionId, in ComponentName app, in FlushMetrics flushMetrics, + in ContentCaptureOptions options, int flushReason); +} 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/view/contentcapture/IContentCaptureDirectManager.aidl b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl index 8d8117bf9ca1..959bf13c55fc 100644 --- a/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl +++ b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl @@ -18,6 +18,7 @@ package android.view.contentcapture; import android.content.pm.ParceledListSlice; import android.view.contentcapture.ContentCaptureEvent; +import android.content.ContentCaptureOptions; /** * Interface between an app (ContentCaptureManager / ContentCaptureSession) and the app providing @@ -26,5 +27,6 @@ import android.view.contentcapture.ContentCaptureEvent; * @hide */ oneway interface IContentCaptureDirectManager { - void sendEvents(in ParceledListSlice events); + // reason and options are used only for metrics logging. + void sendEvents(in ParceledListSlice events, int reason, in ContentCaptureOptions options); } diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index 7241664602e9..c5a5f7360321 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -498,7 +498,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } final ParceledListSlice<ContentCaptureEvent> events = clearEvents(); - mDirectServiceInterface.sendEvents(events); + mDirectServiceInterface.sendEvents(events, reason, mManager.mOptions); } catch (RemoteException e) { Log.w(TAG, "Error sending " + numberEvents + " for " + getDebugState() + ": " + e); 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/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/ZygoteConfig.java b/core/java/com/android/internal/os/ZygoteConfig.java index c6af8c2f4316..6ebcae182b11 100644 --- a/core/java/com/android/internal/os/ZygoteConfig.java +++ b/core/java/com/android/internal/os/ZygoteConfig.java @@ -34,4 +34,7 @@ public class ZygoteConfig { /** The minimum number of processes to keep in the USAP pool */ public static final String USAP_POOL_SIZE_MIN = "usap_pool_size_min"; + + /** The number of milliseconds to delay before refilling the USAP pool */ + public static final String USAP_POOL_REFILL_DELAY_MS = "usap_pool_refill_delay_ms"; } 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..492bc94804fe 100644 --- a/core/java/com/android/internal/os/ZygoteServer.java +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -66,6 +66,12 @@ class ZygoteServer { /** The default value used for the USAP_POOL_SIZE_MIN device property */ private static final String USAP_POOL_SIZE_MIN_DEFAULT = "1"; + /** The default value used for the USAP_REFILL_DELAY_MS device property */ + private static final String USAP_POOL_REFILL_DELAY_MS_DEFAULT = "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 @@ -131,6 +137,18 @@ class ZygoteServer { */ private int mUsapPoolRefillThreshold = 0; + /** + * Number of milliseconds to delay before refilling the pool if it hasn't reached its + * minimum value. + */ + private int mUsapPoolRefillDelayMs = -1; + + private enum UsapPoolRefillAction { + DELAYED, + IMMEDIATE, + NONE + } + ZygoteServer() { mUsapPoolEventFD = null; mZygoteSocket = null; @@ -267,6 +285,13 @@ class ZygoteServer { mUsapPoolSizeMax); } + final String usapPoolRefillDelayMsPropString = Zygote.getConfigurationProperty( + ZygoteConfig.USAP_POOL_REFILL_DELAY_MS, USAP_POOL_REFILL_DELAY_MS_DEFAULT); + + if (!usapPoolRefillDelayMsPropString.isEmpty()) { + mUsapPoolRefillDelayMs = Integer.parseInt(usapPoolRefillDelayMsPropString); + } + // Sanity check if (mUsapPoolSizeMin >= mUsapPoolSizeMax) { Log.w(TAG, "The max size of the USAP pool must be greater than the minimum size." @@ -293,9 +318,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 +335,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 +397,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 +416,8 @@ class ZygoteServer { socketFDs.add(mZygoteSocket.getFileDescriptor()); peers.add(null); + long usapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP; + while (true) { fetchUsapPoolPolicyPropsWithMinInterval(); @@ -428,140 +469,192 @@ class ZygoteServer { } } + int pollTimeoutMs; + + if (usapPoolRefillTriggerTimestamp == INVALID_TIMESTAMP) { + pollTimeoutMs = -1; + } else { + int elapsedTimeMs = + (int) (System.currentTimeMillis() - usapPoolRefillTriggerTimestamp); + + if (elapsedTimeMs >= mUsapPoolRefillDelayMs) { + // 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 = mUsapPoolRefillDelayMs - 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/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml index d90352426311..d4c3565ed0a6 100644 --- a/core/res/res/layout/autofill_save.xml +++ b/core/res/res/layout/autofill_save.xml @@ -26,17 +26,17 @@ android:id="@+id/autofill_save" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:layout_marginTop="32dp" - android:paddingTop="16dp" - android:elevation="32dp" + android:layout_marginTop="@dimen/autofill_save_outer_top_margin" + android:paddingTop="@dimen/autofill_save_outer_top_padding" + android:elevation="@dimen/autofill_elevation" android:background="?android:attr/colorBackground" android:orientation="vertical"> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" - android:paddingStart="16dp" - android:paddingEnd="16dp" + android:paddingStart="@dimen/autofill_save_inner_padding" + android:paddingEnd="@dimen/autofill_save_inner_padding" android:orientation="vertical"> <LinearLayout @@ -47,17 +47,16 @@ <ImageView android:id="@+id/autofill_save_icon" android:scaleType="fitStart" - android:layout_width="24dp" - android:layout_height="24dp"/> + android:layout_width="@dimen/autofill_save_icon_size" + android:layout_height="@dimen/autofill_save_icon_size"/> <TextView android:id="@+id/autofill_save_title" - android:paddingStart="8dp" + android:paddingStart="@dimen/autofill_save_title_start_padding" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/autofill_save_title" - android:textSize="16sp" - android:textColor="?android:attr/textColorPrimary" + android:textAppearance="@style/TextAppearance.DeviceDefault.Subhead" android:layout_weight="1"> </TextView> @@ -67,7 +66,7 @@ android:id="@+id/autofill_save_custom_subtitle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="4dp" + android:layout_marginTop="@dimen/autofill_save_scroll_view_top_margin" android:visibility="gone"/> </LinearLayout> @@ -76,7 +75,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end" - android:padding="16dp" + android:padding="@dimen/autofill_save_button_bar_padding" android:clipToPadding="false" android:layout_weight="1" android:orientation="horizontal"> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 167e6727df3d..6f11432bc5aa 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -73,6 +73,11 @@ <dimen name="navigation_bar_width_car_mode">96dp</dimen> <!-- Height of notification icons in the status bar --> <dimen name="status_bar_icon_size">22dip</dimen> + <!-- Desired size of system icons in status bar. --> + <dimen name="status_bar_system_icon_size">15dp</dimen> + <!-- Intrinsic size of most system icons in status bar. This is the default value that + is used if a Drawable reports an intrinsic size of 0. --> + <dimen name="status_bar_system_icon_intrinsic_size">17dp</dimen> <!-- Size of the giant number (unread count) in the notifications --> <dimen name="status_bar_content_number_size">48sp</dimen> <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. --> @@ -668,6 +673,16 @@ <dimen name="autofill_dataset_picker_max_width">90%</dimen> <dimen name="autofill_dataset_picker_max_height">90%</dimen> + <!-- Autofill save dialog padding --> + <dimen name="autofill_save_outer_top_margin">32dp</dimen> + <dimen name="autofill_save_outer_top_padding">16dp</dimen> + <dimen name="autofill_elevation">32dp</dimen> + <dimen name="autofill_save_inner_padding">16dp</dimen> + <dimen name="autofill_save_icon_size">24dp</dimen> + <dimen name="autofill_save_title_start_padding">8dp</dimen> + <dimen name="autofill_save_scroll_view_top_margin">4dp</dimen> + <dimen name="autofill_save_button_bar_padding">16dp</dimen> + <!-- Max height of the the autofill save custom subtitle as a fraction of the screen width/height --> <dimen name="autofill_save_custom_subtitle_max_height">20%</dimen> 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 7de6ca5c0708..37678dd42512 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -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] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 1ef2eb4a0435..3a348f05de48 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2279,6 +2279,8 @@ <java-symbol type="bool" name="config_alwaysUseCdmaRssi" /> <java-symbol type="dimen" name="status_bar_icon_size" /> + <java-symbol type="dimen" name="status_bar_system_icon_size" /> + <java-symbol type="dimen" name="status_bar_system_icon_intrinsic_size" /> <java-symbol type="drawable" name="list_selector_pressed_holo_dark" /> <java-symbol type="drawable" name="scrubber_control_disabled_holo" /> <java-symbol type="drawable" name="scrubber_control_selector_holo" /> 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/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/EncodedBuffer_test.cpp b/libs/protoutil/tests/EncodedBuffer_test.cpp index 398af609c083..f895154c4983 100644 --- a/libs/protoutil/tests/EncodedBuffer_test.cpp +++ b/libs/protoutil/tests/EncodedBuffer_test.cpp @@ -29,101 +29,101 @@ static void expectPointer(EncodedBuffer::Pointer* p, size_t pos) { } TEST(EncodedBufferTest, WriteSimple) { - EncodedBuffer buffer(TEST_CHUNK_SIZE); - EXPECT_EQ(buffer.size(), 0UL); - expectPointer(buffer.wp(), 0); - EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_SIZE); + sp<EncodedBuffer> buffer = new EncodedBuffer(TEST_CHUNK_SIZE); + EXPECT_EQ(buffer->size(), 0UL); + expectPointer(buffer->wp(), 0); + EXPECT_EQ(buffer->currentToWrite(), TEST_CHUNK_SIZE); for (size_t i = 0; i < TEST_CHUNK_HALF_SIZE; i++) { - buffer.writeRawByte(50 + i); + buffer->writeRawByte(50 + i); } - EXPECT_EQ(buffer.size(), TEST_CHUNK_HALF_SIZE); - expectPointer(buffer.wp(), TEST_CHUNK_HALF_SIZE); - EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_HALF_SIZE); + EXPECT_EQ(buffer->size(), TEST_CHUNK_HALF_SIZE); + expectPointer(buffer->wp(), TEST_CHUNK_HALF_SIZE); + EXPECT_EQ(buffer->currentToWrite(), TEST_CHUNK_HALF_SIZE); for (size_t i = 0; i < TEST_CHUNK_SIZE; i++) { - buffer.writeRawByte(80 + i); + buffer->writeRawByte(80 + i); } - EXPECT_EQ(buffer.size(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE); - expectPointer(buffer.wp(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE); - EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_HALF_SIZE); + EXPECT_EQ(buffer->size(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE); + expectPointer(buffer->wp(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE); + EXPECT_EQ(buffer->currentToWrite(), TEST_CHUNK_HALF_SIZE); // verifies the buffer's data - expectPointer(buffer.ep(), 0); + expectPointer(buffer->ep(), 0); for (size_t i = 0; i < TEST_CHUNK_HALF_SIZE; i++) { - EXPECT_EQ(buffer.readRawByte(), 50 + i); + EXPECT_EQ(buffer->readRawByte(), 50 + i); } for (size_t i = 0; i < TEST_CHUNK_SIZE; i++) { - EXPECT_EQ(buffer.readRawByte(), 80 + i); + EXPECT_EQ(buffer->readRawByte(), 80 + i); } // clears the buffer - buffer.clear(); - EXPECT_EQ(buffer.size(), 0UL); - expectPointer(buffer.wp(), 0); + buffer->clear(); + EXPECT_EQ(buffer->size(), 0UL); + expectPointer(buffer->wp(), 0); } TEST(EncodedBufferTest, WriteVarint) { - EncodedBuffer buffer(TEST_CHUNK_SIZE); + sp<EncodedBuffer> buffer = new EncodedBuffer(TEST_CHUNK_SIZE); size_t expected_buffer_size = 0; - EXPECT_EQ(buffer.writeRawVarint32(13), 1); + EXPECT_EQ(buffer->writeRawVarint32(13), 1); expected_buffer_size += 1; - EXPECT_EQ(buffer.size(), expected_buffer_size); - EXPECT_EQ(buffer.writeRawVarint32(UINT32_C(-1)), 5); + EXPECT_EQ(buffer->size(), expected_buffer_size); + EXPECT_EQ(buffer->writeRawVarint32(UINT32_C(-1)), 5); expected_buffer_size += 5; - EXPECT_EQ(buffer.size(), expected_buffer_size); + EXPECT_EQ(buffer->size(), expected_buffer_size); - EXPECT_EQ(buffer.writeRawVarint64(200), 2); + EXPECT_EQ(buffer->writeRawVarint64(200), 2); expected_buffer_size += 2; - EXPECT_EQ(buffer.size(), expected_buffer_size); - EXPECT_EQ(buffer.writeRawVarint64(UINT64_C(-1)), 10); + EXPECT_EQ(buffer->size(), expected_buffer_size); + EXPECT_EQ(buffer->writeRawVarint64(UINT64_C(-1)), 10); expected_buffer_size += 10; - EXPECT_EQ(buffer.size(), expected_buffer_size); + EXPECT_EQ(buffer->size(), expected_buffer_size); - buffer.writeRawFixed32(UINT32_C(-1)); + buffer->writeRawFixed32(UINT32_C(-1)); expected_buffer_size += 4; - EXPECT_EQ(buffer.size(), expected_buffer_size); - buffer.writeRawFixed64(UINT64_C(-1)); + EXPECT_EQ(buffer->size(), expected_buffer_size); + buffer->writeRawFixed64(UINT64_C(-1)); expected_buffer_size += 8; - EXPECT_EQ(buffer.size(), expected_buffer_size); + EXPECT_EQ(buffer->size(), expected_buffer_size); - EXPECT_EQ(buffer.writeHeader(32, 2), 2); + EXPECT_EQ(buffer->writeHeader(32, 2), 2); expected_buffer_size += 2; - EXPECT_EQ(buffer.size(), expected_buffer_size); + EXPECT_EQ(buffer->size(), expected_buffer_size); // verify data are correctly written to the buffer. - expectPointer(buffer.ep(), 0); - EXPECT_EQ(buffer.readRawVarint(), UINT32_C(13)); - EXPECT_EQ(buffer.readRawVarint(), UINT32_C(-1)); - EXPECT_EQ(buffer.readRawVarint(), UINT64_C(200)); - EXPECT_EQ(buffer.readRawVarint(), UINT64_C(-1)); - EXPECT_EQ(buffer.readRawFixed32(), UINT32_C(-1)); - EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(-1)); - EXPECT_EQ(buffer.readRawVarint(), UINT64_C((32 << 3) + 2)); - expectPointer(buffer.ep(), expected_buffer_size); + expectPointer(buffer->ep(), 0); + EXPECT_EQ(buffer->readRawVarint(), UINT32_C(13)); + EXPECT_EQ(buffer->readRawVarint(), UINT32_C(-1)); + EXPECT_EQ(buffer->readRawVarint(), UINT64_C(200)); + EXPECT_EQ(buffer->readRawVarint(), UINT64_C(-1)); + EXPECT_EQ(buffer->readRawFixed32(), UINT32_C(-1)); + EXPECT_EQ(buffer->readRawFixed64(), UINT64_C(-1)); + EXPECT_EQ(buffer->readRawVarint(), UINT64_C((32 << 3) + 2)); + expectPointer(buffer->ep(), expected_buffer_size); } TEST(EncodedBufferTest, Edit) { - EncodedBuffer buffer(TEST_CHUNK_SIZE); - buffer.writeRawFixed64(0xdeadbeefdeadbeef); - EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0xdeadbeefdeadbeef)); + sp<EncodedBuffer> buffer = new EncodedBuffer(TEST_CHUNK_SIZE); + buffer->writeRawFixed64(0xdeadbeefdeadbeef); + EXPECT_EQ(buffer->readRawFixed64(), UINT64_C(0xdeadbeefdeadbeef)); - buffer.editRawFixed32(4, 0x12345678); + buffer->editRawFixed32(4, 0x12345678); // fixed 64 is little endian order. - buffer.ep()->rewind(); // rewind ep for readRawFixed64 from 0 - EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0x12345678deadbeef)); - - buffer.wp()->rewind(); - expectPointer(buffer.wp(), 0); - buffer.copy(4, 3); - buffer.ep()->rewind(); // rewind ep for readRawFixed64 from 0 - EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0x12345678de345678)); + buffer->ep()->rewind(); // rewind ep for readRawFixed64 from 0 + EXPECT_EQ(buffer->readRawFixed64(), UINT64_C(0x12345678deadbeef)); + + buffer->wp()->rewind(); + expectPointer(buffer->wp(), 0); + buffer->copy(4, 3); + buffer->ep()->rewind(); // rewind ep for readRawFixed64 from 0 + EXPECT_EQ(buffer->readRawFixed64(), UINT64_C(0x12345678de345678)); } TEST(EncodedBufferTest, ReadSimple) { - EncodedBuffer buffer(TEST_CHUNK_SIZE); + sp<EncodedBuffer> buffer = new EncodedBuffer(TEST_CHUNK_SIZE); for (size_t i = 0; i < TEST_CHUNK_3X_SIZE; i++) { - buffer.writeRawByte(i); + buffer->writeRawByte(i); } - sp<ProtoReader> reader1 = buffer.read(); + sp<ProtoReader> reader1 = buffer->read(); EXPECT_EQ(reader1->size(), TEST_CHUNK_3X_SIZE); EXPECT_EQ(reader1->bytesRead(), 0); @@ -132,7 +132,7 @@ TEST(EncodedBufferTest, ReadSimple) { } EXPECT_EQ(reader1->bytesRead(), TEST_CHUNK_3X_SIZE); - sp<ProtoReader> reader2 = buffer.read(); + sp<ProtoReader> reader2 = buffer->read(); uint8_t val = 0; while (reader2->hasNext()) { EXPECT_EQ(reader2->next(), val); @@ -143,10 +143,10 @@ TEST(EncodedBufferTest, ReadSimple) { } TEST(EncodedBufferTest, ReadVarint) { - EncodedBuffer buffer; + sp<EncodedBuffer> buffer = new EncodedBuffer(); uint64_t val = UINT64_C(1522865904593); - size_t len = buffer.writeRawVarint64(val); - sp<ProtoReader> reader = buffer.read(); + size_t len = buffer->writeRawVarint64(val); + sp<ProtoReader> reader = buffer->read(); EXPECT_EQ(reader->size(), len); EXPECT_EQ(reader->readRawVarint(), val); } 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/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index 68c0a22b30c7..435d8d766149 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -509,35 +509,16 @@ public class RingtoneManager { * @return The position of the {@link Uri}, or -1 if it cannot be found. */ public int getRingtonePosition(Uri ringtoneUri) { - if (ringtoneUri == null) return -1; + final long ringtoneId = ContentUris.parseId(ringtoneUri); final Cursor cursor = getCursor(); - final int cursorCount = cursor.getCount(); - - if (!cursor.moveToFirst()) { - return -1; - } - - // Only create Uri objects when the actual URI changes - Uri currentUri = null; - String previousUriString = null; - for (int i = 0; i < cursorCount; i++) { - String uriString = cursor.getString(URI_COLUMN_INDEX); - if (currentUri == null || !uriString.equals(previousUriString)) { - currentUri = Uri.parse(uriString); + cursor.moveToPosition(-1); + while (cursor.moveToNext()) { + if (ringtoneId == cursor.getLong(ID_COLUMN_INDEX)) { + return cursor.getPosition(); } - - if (ringtoneUri.equals(ContentUris.withAppendedId(currentUri, cursor - .getLong(ID_COLUMN_INDEX)))) { - return i; - } - - cursor.move(1); - - previousUriString = uriString; } - return -1; } 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/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/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 1d351a5e53b6..9a95288a69ae 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -826,11 +826,22 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } /** - * @return resource for string that discribes the connection state of this device. - * case 1: idle or playing media, show "Active" on the only one A2DP active device. - * case 2: in phone call, show "Active" on the only one HFP active device + * Return full summary that describes connection state of this device + * + * @see #getConnectionSummary(boolean shortSummary) */ public String getConnectionSummary() { + return getConnectionSummary(false /* shortSummary */); + } + + /** + * Return summary that describes connection state of this device. Summary depends on: + * 1. Whether device has battery info + * 2. Whether device is in active usage(or in phone call) + * + * @param shortSummary {@code true} if need to return short version summary + */ + public String getConnectionSummary(boolean shortSummary) { boolean profileConnected = false; // Updated as long as BluetoothProfile is connected boolean a2dpConnected = true; // A2DP is connected boolean hfpConnected = true; // HFP is connected @@ -909,9 +920,9 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> if ((mIsActiveDeviceHearingAid) || (mIsActiveDeviceHeadset && isOnCall) || (mIsActiveDeviceA2dp && !isOnCall)) { - if (isTwsBatteryAvailable(leftBattery, rightBattery)) { + if (isTwsBatteryAvailable(leftBattery, rightBattery) && !shortSummary) { stringRes = R.string.bluetooth_active_battery_level_untethered; - } else if (batteryLevelPercentageString != null) { + } else if (batteryLevelPercentageString != null && !shortSummary) { stringRes = R.string.bluetooth_active_battery_level; } else { stringRes = R.string.bluetooth_active_no_battery_level; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java index c0a1f11a0a87..93dcbfeab172 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java @@ -192,6 +192,27 @@ public class CachedBluetoothDeviceTest { } @Test + public void getConnectionSummary_shortSummary_returnShortSummary() { + // Test without battery level + // Set A2DP profile to be connected and test connection state summary + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); + assertThat(mCachedDevice.getConnectionSummary(true /* shortSummary */)).isNull(); + + // Set device as Active for A2DP and test connection state summary + mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP); + assertThat(mCachedDevice.getConnectionSummary(true /* shortSummary */)).isEqualTo("Active"); + + // Test with battery level + mBatteryLevel = 10; + assertThat(mCachedDevice.getConnectionSummary(true /* shortSummary */)).isEqualTo( + "Active"); + + // Set A2DP profile to be disconnected and test connection state summary + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); + assertThat(mCachedDevice.getConnectionSummary()).isNull(); + } + + @Test public void getConnectionSummary_testA2dpBatteryInactive_returnBattery() { // Arrange: // 1. Profile: {A2DP, CONNECTED, Inactive} diff --git a/packages/SystemUI/res/layout-land/global_actions_grid.xml b/packages/SystemUI/res/layout-land/global_actions_grid.xml index 511910ea2f61..4619430d6b47 100644 --- a/packages/SystemUI/res/layout-land/global_actions_grid.xml +++ b/packages/SystemUI/res/layout-land/global_actions_grid.xml @@ -5,19 +5,20 @@ 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:gravity="right | center_vertical" 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_height="wrap_content" 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..4ece03b9b8e3 100644 --- a/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml +++ b/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml @@ -5,18 +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="left" - android:paddingRight="@dimen/global_actions_top_padding" + android:gravity="left | center_vertical" 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" + android:layout_height="wrap_content" android:layout_width="wrap_content" - 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/global_actions_grid.xml b/packages/SystemUI/res/layout/global_actions_grid.xml index 3f10b388fdd5..3928062e43d2 100644 --- a/packages/SystemUI/res/layout/global_actions_grid.xml +++ b/packages/SystemUI/res/layout/global_actions_grid.xml @@ -5,17 +5,19 @@ 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:gravity="bottom | center_horizontal" 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:layout_width="wrap_content" 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/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..e00e5f23dea8 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -41,10 +41,10 @@ <!-- Size of the nav bar edge panels, should be greater to the edge sensitivity + the drag threshold --> - <dimen name="navigation_edge_panel_width">76dp</dimen> + <dimen name="navigation_edge_panel_width">70dp</dimen> <!-- Padding at the end of the navigation panel to allow the arrow not to be clipped off --> - <dimen name="navigation_edge_panel_padding">24dp</dimen> - <dimen name="navigation_edge_panel_height">84dp</dimen> + <dimen name="navigation_edge_panel_padding">8dp</dimen> + <dimen name="navigation_edge_panel_height">96dp</dimen> <!-- The threshold to drag to trigger the edge action --> <dimen name="navigation_edge_action_drag_threshold">16dp</dimen> <!-- The minimum display position of the arrow on the screen --> @@ -89,7 +89,7 @@ <dimen name="status_bar_wifi_signal_spacer_width">2.5dp</dimen> <!-- Size of the view displaying the wifi signal icon in the status bar. --> - <dimen name="status_bar_wifi_signal_size">15dp</dimen> + <dimen name="status_bar_wifi_signal_size">@*android:dimen/status_bar_system_icon_size</dimen> <!-- Spacing before the airplane mode icon if there are any icons preceding it. --> <dimen name="status_bar_airplane_spacer_width">4dp</dimen> @@ -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/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/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java index 45c19addd1de..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); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index 8aad0f8bd831..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,6 +99,10 @@ class Bubble { return entry.notification.getPackageName(); } + public String getAppName() { + return mAppName; + } + boolean isInflated() { return mInflated; } @@ -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..dcc0419ab0cf 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -40,6 +40,7 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; import android.app.Notification; +import android.app.NotificationManager; import android.content.Context; import android.content.pm.ParceledListSlice; import android.content.res.Configuration; @@ -193,7 +194,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (shouldCollapse) { collapseStack(); } - updateVisibility(); + updateStack(); } } @@ -381,6 +382,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi * @param notif the notification associated with this bubble. */ void updateBubble(NotificationEntry notif) { + // If this is an interruptive notif, mark that it's interrupted + if (notif.importance >= NotificationManager.IMPORTANCE_HIGH) { + notif.setInterruption(); + } mBubbleData.notificationEntryUpdated(notif); } @@ -534,10 +539,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 +560,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/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 4fef157183c2..4ad3a332ebe6 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -23,6 +23,7 @@ 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.graphics.ColorMatrix; @@ -553,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); 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/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 4590470697ea..35b8d203cbe2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -30,6 +30,7 @@ import android.graphics.drawable.Icon; import android.icu.text.DateFormat; import android.icu.text.DisplayContext; import android.media.MediaMetadata; +import android.media.session.PlaybackState; import android.net.Uri; import android.os.Handler; import android.os.Trace; @@ -57,6 +58,7 @@ import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.policy.ZenModeControllerImpl; import java.util.Date; +import java.util.HashSet; import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.TimeUnit; @@ -98,6 +100,7 @@ public class KeyguardSliceProvider extends SliceProvider implements private final Date mCurrentTime = new Date(); private final Handler mHandler; private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm; + private final HashSet<Integer> mMediaInvisibleStates; private ZenModeController mZenModeController; private String mDatePattern; private DateFormat mDateFormat; @@ -113,6 +116,7 @@ public class KeyguardSliceProvider extends SliceProvider implements private StatusBarStateController mStatusBarStateController; protected MediaMetadata mMediaMetaData; protected boolean mDozing; + private boolean mMediaIsVisible; /** * Receiver responsible for time ticking and updating the date format. @@ -169,6 +173,11 @@ public class KeyguardSliceProvider extends SliceProvider implements mAlarmUri = Uri.parse(KEYGUARD_NEXT_ALARM_URI); mDndUri = Uri.parse(KEYGUARD_DND_URI); mMediaUri = Uri.parse(KEYGUARD_MEDIA_URI); + + mMediaInvisibleStates = new HashSet<>(); + mMediaInvisibleStates.add(PlaybackState.STATE_NONE); + mMediaInvisibleStates.add(PlaybackState.STATE_STOPPED); + mMediaInvisibleStates.add(PlaybackState.STATE_PAUSED); } /** @@ -209,31 +218,33 @@ public class KeyguardSliceProvider extends SliceProvider implements } protected boolean needsMediaLocked() { - return mMediaMetaData != null && mDozing; + return mMediaMetaData != null && mMediaIsVisible && mDozing; } protected void addMediaLocked(ListBuilder listBuilder) { - if (mMediaMetaData != null) { - CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE); - if (TextUtils.isEmpty(title)) { - title = getContext().getResources().getString(R.string.music_controls_no_title); - } - listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(title)); - - CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST); - if (!TextUtils.isEmpty(album)) { - RowBuilder albumBuilder = new RowBuilder(mMediaUri); - albumBuilder.setTitle(album); - - Icon mediaIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon(); - IconCompat mediaIconCompat = mediaIcon == null ? null - : IconCompat.createFromIcon(getContext(), mediaIcon); - if (mediaIconCompat != null) { - albumBuilder.addEndItem(mediaIconCompat, ListBuilder.ICON_IMAGE); - } + if (mMediaMetaData == null) { + return; + } - listBuilder.addRow(albumBuilder); + CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE); + if (TextUtils.isEmpty(title)) { + title = getContext().getResources().getString(R.string.music_controls_no_title); + } + listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(title)); + + CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST); + if (!TextUtils.isEmpty(album)) { + RowBuilder albumBuilder = new RowBuilder(mMediaUri); + albumBuilder.setTitle(album); + + Icon mediaIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon(); + IconCompat mediaIconCompat = mediaIcon == null ? null + : IconCompat.createFromIcon(getContext(), mediaIcon); + if (mediaIconCompat != null) { + albumBuilder.addEndItem(mediaIconCompat, ListBuilder.ICON_IMAGE); } + + listBuilder.addRow(albumBuilder); } } @@ -411,9 +422,14 @@ public class KeyguardSliceProvider extends SliceProvider implements * @param metadata New metadata. */ @Override - public void onMetadataChanged(MediaMetadata metadata) { + public void onMetadataOrStateChanged(MediaMetadata metadata, @PlaybackState.State int state) { synchronized (this) { + boolean nextVisible = !mMediaInvisibleStates.contains(state); + if (nextVisible == mMediaIsVisible && metadata == mMediaMetaData) { + return; + } mMediaMetaData = metadata; + mMediaIsVisible = nextVisible; } notifyChange(); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java index af2b7677dcad..a9fe54bae19d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java @@ -21,7 +21,6 @@ import static android.app.ActivityManager.TaskDescription; import android.annotation.ColorInt; import android.annotation.UserIdInt; import android.app.Activity; -import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.KeyguardManager; import android.app.PendingIntent; @@ -32,9 +31,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.graphics.Color; import android.os.Bundle; -import android.os.RemoteException; import android.os.UserHandle; -import android.util.Log; import android.view.View; import com.android.internal.annotations.VisibleForTesting; @@ -56,7 +53,9 @@ public class WorkLockActivity extends Activity { */ static final String EXTRA_TASK_DESCRIPTION = "com.android.systemui.keyguard.extra.TASK_DESCRIPTION"; - + + private static final int REQUEST_CODE_CONFIRM_CREDENTIALS = 1; + /** * Cached keyguard manager instance populated by {@link #getKeyguardManager}. * @see KeyguardManager @@ -111,7 +110,6 @@ public class WorkLockActivity extends Activity { @Override public void onBackPressed() { // Ignore back presses. - return; } @Override @@ -151,26 +149,26 @@ public class WorkLockActivity extends Activity { PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE, options.toBundle()); - credential.putExtra(Intent.EXTRA_INTENT, target.getIntentSender()); - try { - ActivityManager.getService().startConfirmDeviceCredentialIntent(credential, - getChallengeOptions().toBundle()); - } catch (RemoteException e) { - Log.e(TAG, "Failed to start confirm credential intent", e); + if (target != null) { + credential.putExtra(Intent.EXTRA_INTENT, target.getIntentSender()); } + + startActivityForResult(credential, REQUEST_CODE_CONFIRM_CREDENTIALS); } - private ActivityOptions getChallengeOptions() { - // If we are taking up the whole screen, just use the default animation of clipping the - // credentials activity into the entire foreground. - if (!isInMultiWindowMode()) { - return ActivityOptions.makeBasic(); + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQUEST_CODE_CONFIRM_CREDENTIALS && resultCode != RESULT_OK) { + // The user dismissed the challenge, don't show it again. + goToHomeScreen(); } + } - // Otherwise, animate the transition from this part of the screen to fullscreen - // using our own decor as the starting position. - final View view = getWindow().getDecorView(); - return ActivityOptions.makeScaleUpAnimation(view, 0, 0, view.getWidth(), view.getHeight()); + private void goToHomeScreen() { + final Intent homeIntent = new Intent(Intent.ACTION_MAIN); + homeIntent.addCategory(Intent.CATEGORY_HOME); + homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(homeIntent); } private KeyguardManager getKeyguardManager() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index b9e0c60fabeb..75ef18545fdf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -150,8 +150,8 @@ public class NotificationMediaManager implements Dumpable { if (state != null) { if (!isPlaybackActive(state.getState())) { clearCurrentMediaNotification(); - dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */); } + dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */); } } @@ -242,7 +242,8 @@ public class NotificationMediaManager implements Dumpable { public void addCallback(MediaListener callback) { mMediaListeners.add(callback); - callback.onMetadataChanged(mMediaMetadata); + callback.onMetadataOrStateChanged(mMediaMetadata, + getMediaControllerPlaybackState(mMediaController)); } public void removeCallback(MediaListener callback) { @@ -357,9 +358,10 @@ public class NotificationMediaManager implements Dumpable { if (mPresenter != null) { mPresenter.updateMediaMetaData(changed, allowEnterAnimation); } + @PlaybackState.State int state = getMediaControllerPlaybackState(mMediaController); ArrayList<MediaListener> callbacks = new ArrayList<>(mMediaListeners); for (int i = 0; i < callbacks.size(); i++) { - callbacks.get(i).onMetadataChanged(mMediaMetadata); + callbacks.get(i).onMetadataOrStateChanged(mMediaMetadata, state); } } @@ -698,6 +700,12 @@ public class NotificationMediaManager implements Dumpable { } public interface MediaListener { - void onMetadataChanged(MediaMetadata metadata); + /** + * Called whenever there's new metadata or playback state. + * @param metadata Current metadata. + * @param state Current playback state + * @see PlaybackState.State + */ + void onMetadataOrStateChanged(MediaMetadata metadata, @PlaybackState.State int state); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 033c4fbed6f7..6552fe671794 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -40,7 +40,6 @@ import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.AttributeSet; -import android.util.DisplayMetrics; import android.util.FloatProperty; import android.util.Log; import android.util.Property; @@ -72,12 +71,12 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi /** * Status icons are currently drawn with the intention of being 17dp tall, but we * want to scale them (in a way that doesn't require an asset dump) down 2dp. So - * 17dp * (15 / 17) = 15dp, the new height. + * 17dp * (15 / 17) = 15dp, the new height. After the first call to {@link #reloadDimens} all + * values will be in px. */ - private static final float SYSTEM_ICON_DESIRED_HEIGHT = 15f; - private static final float SYSTEM_ICON_INTRINSIC_HEIGHT = 17f; - private static final float SYSTEM_ICON_SCALE = - SYSTEM_ICON_DESIRED_HEIGHT / SYSTEM_ICON_INTRINSIC_HEIGHT; + private float mSystemIconDesiredHeight = 15f; + private float mSystemIconIntrinsicHeight = 17f; + private float mSystemIconDefaultScale = mSystemIconDesiredHeight / mSystemIconIntrinsicHeight; private final int ANIMATION_DURATION_FAST = 100; public static final int STATE_ICON = 0; @@ -209,21 +208,20 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi // Makes sure that all icons are scaled to the same height (15dp). If we cannot get a height // for the icon, it uses the default SCALE (15f / 17f) which is the old behavior private void updateIconScaleForSystemIcons() { - float iconHeight = getIconHeightInDps(); + float iconHeight = getIconHeight(); if (iconHeight != 0) { - mIconScale = SYSTEM_ICON_DESIRED_HEIGHT / iconHeight; + mIconScale = mSystemIconDesiredHeight / iconHeight; } else { - mIconScale = SYSTEM_ICON_SCALE; + mIconScale = mSystemIconDefaultScale; } } - private float getIconHeightInDps() { + private float getIconHeight() { Drawable d = getDrawable(); if (d != null) { - return ((float) getDrawable().getIntrinsicHeight() * DisplayMetrics.DENSITY_DEFAULT) - / mDensity; + return (float) getDrawable().getIntrinsicHeight(); } else { - return SYSTEM_ICON_INTRINSIC_HEIGHT; + return mSystemIconIntrinsicHeight; } } @@ -265,6 +263,11 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi if (applyRadius) { mDotRadius = mStaticDotRadius; } + mSystemIconDesiredHeight = res.getDimension( + com.android.internal.R.dimen.status_bar_system_icon_size); + mSystemIconIntrinsicHeight = res.getDimension( + com.android.internal.R.dimen.status_bar_system_icon_intrinsic_size); + mSystemIconDefaultScale = mSystemIconDesiredHeight / mSystemIconIntrinsicHeight; } public void setNotification(StatusBarNotification notification) { @@ -272,6 +275,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi if (notification != null) { setContentDescription(notification.getNotification()); } + maybeUpdateIconScaleDimens(); } public StatusBarIconView(Context context, AttributeSet attrs) { @@ -280,7 +284,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi mBlocked = false; mAlwaysScaleIcon = true; reloadDimens(); - updateIconScaleForNotifications(); + maybeUpdateIconScaleDimens(); mDensity = context.getResources().getDisplayMetrics().densityDpi; } @@ -854,7 +858,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi public void setDark(boolean dark, boolean fade, long delay) { mDozer.setIntensityDark(f -> { mDarkAmount = f; - updateIconScaleForNotifications(); + maybeUpdateIconScaleDimens(); updateDecorColor(); updateIconColor(); updateAllowAnimation(); 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/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java index 4603ba6af89f..4f223c385eb4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java @@ -25,6 +25,7 @@ import android.graphics.Path; import android.graphics.Rect; import android.os.SystemClock; import android.os.VibrationEffect; +import android.util.DisplayMetrics; import android.util.MathUtils; import android.view.ContextThemeWrapper; import android.view.MotionEvent; @@ -47,14 +48,14 @@ import androidx.dynamicanimation.animation.SpringForce; public class NavigationBarEdgePanel extends View { - private static final long COLOR_ANIMATION_DURATION_MS = 100; - private static final long DISAPPEAR_FADE_ANIMATION_DURATION_MS = 140; + private static final long COLOR_ANIMATION_DURATION_MS = 120; + private static final long DISAPPEAR_FADE_ANIMATION_DURATION_MS = 80; private static final long DISAPPEAR_ARROW_ANIMATION_DURATION_MS = 100; /** - * The minimum time required since the first vibration effect to receive a second one + * The time required since the first vibration effect to automatically trigger a click */ - private static final int MIN_TIME_BETWEEN_EFFECTS_MS = 120; + private static final int GESTURE_DURATION_FOR_CLICK_MS = 400; /** * The size of the protection of the arrow in px. Only used if this is not background protected @@ -79,7 +80,7 @@ public class NavigationBarEdgePanel extends View { /** * The angle that is added per 1000 px speed to the angle of the leg */ - private static final int ARROW_ANGLE_ADDED_PER_1000_SPEED = 8; + private static final int ARROW_ANGLE_ADDED_PER_1000_SPEED = 4; /** * The maximum angle offset allowed due to speed @@ -92,15 +93,15 @@ public class NavigationBarEdgePanel extends View { private static final float ARROW_THICKNESS_DP = 2.5f; /** - * The amount of rubber banding we do for the horizontal translation beyond the base translation + * The amount of rubber banding we do for the vertical translation */ - private static final int RUBBER_BAND_AMOUNT = 10; + private static final int RUBBER_BAND_AMOUNT = 15; /** * The interpolator used to rubberband */ private static final Interpolator RUBBER_BAND_INTERPOLATOR - = new PathInterpolator(1.0f / RUBBER_BAND_AMOUNT, 1.0f, 1.0f, 1.0f); + = new PathInterpolator(1.0f / 5.0f, 1.0f, 1.0f, 1.0f); /** * The amount of rubber banding we do for the translation before base translation @@ -189,6 +190,7 @@ public class NavigationBarEdgePanel extends View { private int mCurrentArrowColor; private float mDisappearAmount; private long mVibrationTime; + private int mScreenSize; private DynamicAnimation.OnAnimationEndListener mSetGoneEndListener = new DynamicAnimation.OnAnimationEndListener() { @@ -281,9 +283,8 @@ public class NavigationBarEdgePanel extends View { mAngleAnimation = new SpringAnimation(this, CURRENT_ANGLE); mAngleAppearForce = new SpringForce() - .setStiffness(SpringForce.STIFFNESS_LOW) - .setDampingRatio(0.4f) - .setFinalPosition(ARROW_ANGLE_WHEN_EXTENDED_DEGREES); + .setStiffness(500) + .setDampingRatio(0.5f); mAngleDisappearForce = new SpringForce() .setStiffness(SpringForce.STIFFNESS_MEDIUM) .setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY) @@ -447,13 +448,14 @@ public class NavigationBarEdgePanel extends View { protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - // TODO: read the gesture length from the nav controller. mMaxTranslation = getWidth() - mArrowPaddingEnd; } private void loadDimens() { mArrowPaddingEnd = getContext().getResources().getDimensionPixelSize( R.dimen.navigation_edge_panel_padding); + DisplayMetrics metrics = getResources().getDisplayMetrics(); + mScreenSize = Math.min(metrics.widthPixels, metrics.heightPixels); } private void updateArrowDirection() { @@ -510,7 +512,7 @@ public class NavigationBarEdgePanel extends View { if (!mArrowsPointLeft) { x = -x; } - float extent = 1.0f - mDisappearAmount; + float extent = MathUtils.lerp(1.0f, 0.75f, mDisappearAmount); x = x * extent; y = y * extent; mArrowPath.reset(); @@ -529,27 +531,29 @@ public class NavigationBarEdgePanel extends View { } private void triggerBack() { - if (SystemClock.uptimeMillis() - mVibrationTime >= MIN_TIME_BETWEEN_EFFECTS_MS) { - mVibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK); - } mVelocityTracker.computeCurrentVelocity(1000); // Only do the extra translation if we're not already flinging - boolean doExtraTranslation = Math.abs(mVelocityTracker.getXVelocity()) < 1000; - if (doExtraTranslation) { - setDesiredTranslation(mDesiredTranslation + dp(16), true /* animate */); + boolean isSlow = Math.abs(mVelocityTracker.getXVelocity()) < 500; + if (isSlow + || SystemClock.uptimeMillis() - mVibrationTime >= GESTURE_DURATION_FOR_CLICK_MS) { + mVibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK); } // Let's also snap the angle a bit - if (mAngleOffset < -4) { - mAngleOffset = Math.max(-16, mAngleOffset - 16); + if (mAngleOffset > -4) { + mAngleOffset = Math.max(-8, mAngleOffset - 8); updateAngle(true /* animated */); } // Finally, after the translation, animate back and disappear the arrow Runnable translationEnd = () -> { - setTriggerBack(false /* false */, true /* animate */); + // let's snap it back + mAngleOffset = Math.max(0, mAngleOffset + 8); + updateAngle(true /* animated */); + mTranslationAnimation.setSpring(mTriggerBackSpring); - setDesiredTranslation(0, true /* animated */); + // Translate the arrow back a bit to make for a nice transition + setDesiredTranslation(mDesiredTranslation - dp(32), true /* animated */); animate().alpha(0f).setDuration(DISAPPEAR_FADE_ANIMATION_DURATION_MS) .withEndAction(() -> setVisibility(GONE)); mArrowDisappearAnimation.start(); @@ -584,6 +588,7 @@ public class NavigationBarEdgePanel extends View { setTriggerBack(false /* triggerBack */, false /* animated */); setDesiredTranslation(0, false /* animated */); setCurrentTranslation(0); + updateAngle(false /* animate */); mPreviousTouchTranslation = 0; mTotalTouchDelta = 0; mVibrationTime = 0; @@ -621,7 +626,7 @@ public class NavigationBarEdgePanel extends View { // Let's make sure we only go to the baseextend and apply rubberbanding afterwards if (touchTranslation > mBaseTranslation) { float diff = touchTranslation - mBaseTranslation; - float progress = MathUtils.saturate(diff / (mBaseTranslation * RUBBER_BAND_AMOUNT)); + float progress = MathUtils.saturate(diff / (mScreenSize - mBaseTranslation)); progress = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress) * (mMaxTranslation - mBaseTranslation); touchTranslation = mBaseTranslation + progress; 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/RegionSamplingHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java index d59319e110de..2b0bb21c6560 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java @@ -27,6 +27,7 @@ import android.provider.Settings; import android.view.CompositionSamplingListener; import android.view.SurfaceControl; import android.view.View; +import android.view.ViewRootImpl; import android.view.ViewTreeObserver; import com.android.systemui.R; @@ -153,8 +154,12 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, boolean isSamplingEnabled = mSamplingEnabled && !mSamplingRequestBounds.isEmpty() && (mSampledView.isAttachedToWindow() || mFirstSamplingAfterStart); if (isSamplingEnabled) { - SurfaceControl stopLayerControl = mSampledView.getViewRootImpl().getSurfaceControl(); - if (!stopLayerControl.isValid()) { + ViewRootImpl viewRootImpl = mSampledView.getViewRootImpl(); + SurfaceControl stopLayerControl = null; + if (viewRootImpl != null) { + stopLayerControl = viewRootImpl.getSurfaceControl(); + } + if (stopLayerControl == null || !stopLayerControl.isValid()) { if (!mWaitingOnDraw) { mWaitingOnDraw = true; // The view might be attached but we haven't drawn yet, so wait until the 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/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java index c534de7e24a3..355e26071b2a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.when; import android.app.AlarmManager; import android.content.ContentResolver; import android.media.MediaMetadata; +import android.media.session.PlaybackState; import android.net.Uri; import android.provider.Settings; import android.testing.AndroidTestingRunner; @@ -105,7 +106,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { MediaMetadata metadata = mock(MediaMetadata.class); when(metadata.getText(any())).thenReturn("metadata"); mProvider.onDozingChanged(true); - mProvider.onMetadataChanged(metadata); + mProvider.onMetadataOrStateChanged(metadata, PlaybackState.STATE_PLAYING); mProvider.onBindSlice(mProvider.getUri()); verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_TITLE)); verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_ARTIST)); @@ -170,7 +171,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { public void onMetadataChanged_updatesSlice() { mProvider.onDozingChanged(true); reset(mContentResolver); - mProvider.onMetadataChanged(mock(MediaMetadata.class)); + mProvider.onMetadataOrStateChanged(mock(MediaMetadata.class), PlaybackState.STATE_PLAYING); verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null)); // Hides after waking up @@ -181,7 +182,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { @Test public void onDozingChanged_updatesSliceIfMedia() { - mProvider.onMetadataChanged(mock(MediaMetadata.class)); + mProvider.onMetadataOrStateChanged(mock(MediaMetadata.class), PlaybackState.STATE_PLAYING); reset(mContentResolver); // Show media when dozing mProvider.onDozingChanged(true); 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/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java new file mode 100644 index 000000000000..dd1b84b16f68 --- /dev/null +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.contentcapture; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.ContentCaptureOptions; +import android.service.contentcapture.FlushMetrics; +import android.util.StatsLog; + +import java.util.List; + +/** @hide */ +public final class ContentCaptureMetricsLogger { + /** + * Class only contains static utility functions, and should not be instantiated + */ + private ContentCaptureMetricsLogger() { + } + + /** @hide */ + public static void writeServiceEvent(int eventType, @NonNull String serviceName, + @Nullable String targetPackage) { + StatsLog.write(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS, eventType, serviceName, + targetPackage); + } + + /** @hide */ + public static void writeServiceEvent(int eventType, @NonNull ComponentName service, + @Nullable ComponentName target) { + writeServiceEvent(eventType, ComponentName.flattenToShortString(service), + ComponentName.flattenToShortString(target)); + } + + /** @hide */ + public static void writeServiceEvent(int eventType, @NonNull ComponentName service, + @Nullable String targetPackage) { + writeServiceEvent(eventType, ComponentName.flattenToShortString(service), targetPackage); + } + + /** @hide */ + public static void writeServiceEvent(int eventType, @NonNull ComponentName service) { + writeServiceEvent(eventType, ComponentName.flattenToShortString(service), null); + } + + /** @hide */ + public static void writeSetWhitelistEvent(@Nullable ComponentName service, + @Nullable List<String> packages, @Nullable List<ComponentName> activities) { + final String serviceName = ComponentName.flattenToShortString(service); + StringBuilder stringBuilder = new StringBuilder(); + if (packages != null && packages.size() > 0) { + final int size = packages.size(); + stringBuilder.append(packages.get(0)); + for (int i = 1; i < size; i++) { + stringBuilder.append(" "); + stringBuilder.append(packages.get(i)); + } + } + if (activities != null && activities.size() > 0) { + stringBuilder.append(" "); + stringBuilder.append(activities.get(0).flattenToShortString()); + final int size = activities.size(); + for (int i = 1; i < size; i++) { + stringBuilder.append(" "); + stringBuilder.append(activities.get(i).flattenToShortString()); + } + } + StatsLog.write(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS, + StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__SET_WHITELIST, + serviceName, stringBuilder.toString()); + } + + /** @hide */ + public static void writeSessionEvent(int sessionId, int event, int flags, + @NonNull ComponentName service, @Nullable ComponentName app, boolean isChildSession) { + StatsLog.write(StatsLog.CONTENT_CAPTURE_SESSION_EVENTS, sessionId, event, flags, + ComponentName.flattenToShortString(service), + ComponentName.flattenToShortString(app), isChildSession); + } + + /** @hide */ + public static void writeSessionFlush(int sessionId, @NonNull ComponentName service, + @Nullable ComponentName app, @NonNull FlushMetrics fm, + @NonNull ContentCaptureOptions options, int flushReason) { + StatsLog.write(StatsLog.CONTENT_CAPTURE_FLUSHED, sessionId, + ComponentName.flattenToShortString(service), + ComponentName.flattenToShortString(app), fm.sessionStarted, fm.sessionFinished, + fm.viewAppearedCount, fm.viewDisappearedCount, fm.viewTextChangedCount, + options.maxBufferSize, options.idleFlushingFrequencyMs, + options.textChangeFlushingFrequencyMs, flushReason); + } +} diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index 67c3d01cb86b..a186d4e7f467 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -24,6 +24,9 @@ import static android.view.contentcapture.ContentCaptureSession.STATE_INTERNAL_E import static android.view.contentcapture.ContentCaptureSession.STATE_NOT_WHITELISTED; import static android.view.contentcapture.ContentCaptureSession.STATE_NO_SERVICE; +import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeServiceEvent; +import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeSessionEvent; +import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeSetWhitelistEvent; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE; @@ -35,6 +38,7 @@ import android.app.ActivityManagerInternal; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; import android.content.ComponentName; +import android.content.ContentCaptureOptions; import android.content.pm.ActivityPresentationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -48,6 +52,7 @@ import android.service.contentcapture.ActivityEvent; import android.service.contentcapture.ActivityEvent.ActivityEventType; import android.service.contentcapture.ContentCaptureService; import android.service.contentcapture.ContentCaptureServiceInfo; +import android.service.contentcapture.FlushMetrics; import android.service.contentcapture.IContentCaptureServiceCallback; import android.service.contentcapture.SnapshotData; import android.util.ArrayMap; @@ -55,6 +60,7 @@ import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.StatsLog; import android.view.contentcapture.ContentCaptureCondition; import android.view.contentcapture.DataRemovalRequest; @@ -231,7 +237,6 @@ final class ContentCapturePerUserService resurrectSessionsLocked(); } - // TODO(b/119613670): log metrics @GuardedBy("mLock") public void startSessionLocked(@NonNull IBinder activityToken, @NonNull ActivityPresentationInfo activityPresentationInfo, int sessionId, int uid, @@ -263,9 +268,14 @@ final class ContentCapturePerUserService if (!enabled) { // TODO: it would be better to split in differet reasons, like - // STATE_DISABLED_NO_SERVICE and STATE_DISABLED_BY_DEVICE_POLICY + // STATE_DISABLED_NO and STATE_DISABLED_BY_DEVICE_POLICY setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE, /* binder= */ null); + // Log metrics. + writeSessionEvent(sessionId, + StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED, + STATE_DISABLED | STATE_NO_SERVICE, serviceComponentName, + componentName, /* isChildSession= */ false); return; } if (serviceComponentName == null) { @@ -285,6 +295,11 @@ final class ContentCapturePerUserService } setClientState(clientReceiver, STATE_DISABLED | STATE_NOT_WHITELISTED, /* binder= */ null); + // Log metrics. + writeSessionEvent(sessionId, + StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED, + STATE_DISABLED | STATE_NOT_WHITELISTED, serviceComponentName, + componentName, /* isChildSession= */ false); return; } @@ -294,6 +309,11 @@ final class ContentCapturePerUserService + ": ignoring because it already exists for " + existingSession.mActivityToken); setClientState(clientReceiver, STATE_DISABLED | STATE_DUPLICATED_ID, /* binder=*/ null); + // Log metrics. + writeSessionEvent(sessionId, + StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED, + STATE_DISABLED | STATE_DUPLICATED_ID, + serviceComponentName, componentName, /* isChildSession= */ false); return; } @@ -302,11 +322,15 @@ final class ContentCapturePerUserService } if (mRemoteService == null) { - // TODO(b/119613670): log metrics Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken + ": ignoring because service is not set"); setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE, /* binder= */ null); + // Log metrics. + writeSessionEvent(sessionId, + StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED, + STATE_DISABLED | STATE_NO_SERVICE, serviceComponentName, + componentName, /* isChildSession= */ false); return; } @@ -324,7 +348,6 @@ final class ContentCapturePerUserService newSession.notifySessionStartedLocked(clientReceiver); } - // TODO(b/119613670): log metrics @GuardedBy("mLock") public void finishSessionLocked(int sessionId) { if (!isEnabledLocked()) { @@ -553,6 +576,7 @@ final class ContentCapturePerUserService + " for user " + mUserId); } mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities); + writeSetWhitelistEvent(getServiceComponentName(), packages, activities); // Must disable session that are not the whitelist anymore... final int numSessions = mSessions.size(); @@ -602,7 +626,6 @@ final class ContentCapturePerUserService mConditionsByPkg.put(packageName, new ArraySet<>(conditions)); } } - // TODO(b/119613670): log metrics } @Override @@ -616,6 +639,15 @@ final class ContentCapturePerUserService } finally { Binder.restoreCallingIdentity(token); } + writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__SET_DISABLED, + getServiceComponentName()); + } + + @Override + public void writeSessionFlush(int sessionId, ComponentName app, FlushMetrics flushMetrics, + ContentCaptureOptions options, int flushReason) { + ContentCaptureMetricsLogger.writeSessionFlush(sessionId, getServiceComponentName(), app, + flushMetrics, options, flushReason); } } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java index 2171033c5a28..18daf325db8c 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java @@ -18,6 +18,9 @@ package com.android.server.contentcapture; import static android.view.contentcapture.ContentCaptureHelper.sDebug; import static android.view.contentcapture.ContentCaptureHelper.sVerbose; +import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeServiceEvent; +import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeSessionEvent; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; @@ -28,6 +31,7 @@ import android.service.contentcapture.IContentCaptureService; import android.service.contentcapture.IContentCaptureServiceCallback; import android.service.contentcapture.SnapshotData; import android.util.Slog; +import android.util.StatsLog; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.DataRemovalRequest; @@ -77,6 +81,8 @@ final class RemoteContentCaptureService if (connected) { try { mService.onConnected(mServerCallback, sVerbose, sDebug); + writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_CONNECTED, + mComponentName); } finally { // Update the system-service state, in case the service reconnected after // dying @@ -84,6 +90,8 @@ final class RemoteContentCaptureService } } else { mService.onDisconnected(); + writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_DISCONNECTED, + mComponentName); } } catch (Exception e) { Slog.w(mTag, "Exception calling onConnectedStateChanged(" + connected + "): " + e); @@ -102,6 +110,10 @@ final class RemoteContentCaptureService @NonNull IResultReceiver clientReceiver, int initialState) { scheduleAsyncRequest( (s) -> s.onSessionStarted(context, sessionId, uid, clientReceiver, initialState)); + // Metrics logging. + writeSessionEvent(sessionId, + StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__ON_SESSION_STARTED, initialState, + getComponentName(), context.getActivityComponent(), /* is_child_session= */ false); } /** @@ -110,6 +122,11 @@ final class RemoteContentCaptureService */ public void onSessionFinished(int sessionId) { scheduleAsyncRequest((s) -> s.onSessionFinished(sessionId)); + // Metrics logging. + writeSessionEvent(sessionId, + StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__ON_SESSION_FINISHED, + /* flags= */ 0, getComponentName(), /* app= */ null, + /* is_child_session= */ false); } /** @@ -124,6 +141,8 @@ final class RemoteContentCaptureService */ public void onDataRemovalRequest(@NonNull DataRemovalRequest request) { scheduleAsyncRequest((s) -> s.onDataRemovalRequest(request)); + writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_USER_DATA_REMOVED, + mComponentName); } /** diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 72899f62a55d..cb61259ec53d 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -1138,8 +1138,8 @@ class AlarmManagerService extends SystemService { ? clampPositive(whenElapsed + a.windowLength) : maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval); } - a.whenElapsed = whenElapsed; - a.maxWhenElapsed = maxElapsed; + a.expectedWhenElapsed = a.whenElapsed = whenElapsed; + a.expectedMaxWhenElapsed = a.maxWhenElapsed = maxElapsed; setImplLocked(a, true, doValidate); } @@ -1243,7 +1243,7 @@ class AlarmManagerService extends SystemService { alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval; // Also schedule its next recurrence final long delta = alarm.count * alarm.repeatInterval; - final long nextElapsed = alarm.whenElapsed + delta; + final long nextElapsed = alarm.expectedWhenElapsed + delta; setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength, maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval), alarm.repeatInterval, alarm.operation, null, null, alarm.flags, true, @@ -3596,10 +3596,9 @@ class AlarmManagerService extends SystemService { // this adjustment will be zero if we're late by // less than one full repeat interval alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval; - // Also schedule its next recurrence final long delta = alarm.count * alarm.repeatInterval; - final long nextElapsed = alarm.whenElapsed + delta; + final long nextElapsed = alarm.expectedWhenElapsed + delta; setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength, maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval), alarm.repeatInterval, alarm.operation, null, null, alarm.flags, true, diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d8f5937d3cdd..ea71a3b2e17e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -735,11 +735,11 @@ public class ActivityManagerService extends IActivityManager.Stub * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this * method. */ - void put(int key, ProcessRecord value) { + void put(ProcessRecord app) { synchronized (this) { - mPidMap.put(key, value); + mPidMap.put(app.pid, app); } - mAtmInternal.onProcessMapped(key, value.getWindowProcessController()); + mAtmInternal.onProcessMapped(app.pid, app.getWindowProcessController()); } /** @@ -747,11 +747,18 @@ public class ActivityManagerService extends IActivityManager.Stub * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this * method. */ - void remove(int pid) { + void remove(ProcessRecord app) { + boolean removed = false; synchronized (this) { - mPidMap.remove(pid); + final ProcessRecord existingApp = mPidMap.get(app.pid); + if (existingApp != null && existingApp.startSeq == app.startSeq) { + mPidMap.remove(app.pid); + removed = true; + } + } + if (removed) { + mAtmInternal.onProcessUnMapped(app.pid); } - mAtmInternal.onProcessUnMapped(pid); } /** @@ -759,17 +766,18 @@ public class ActivityManagerService extends IActivityManager.Stub * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this * method. */ - boolean removeIfNoThread(int pid) { + boolean removeIfNoThread(ProcessRecord app) { boolean removed = false; synchronized (this) { - final ProcessRecord app = get(pid); - if (app != null && app.thread == null) { - mPidMap.remove(pid); + final ProcessRecord existingApp = get(app.pid); + if (existingApp != null && existingApp.startSeq == app.startSeq + && app.thread == null) { + mPidMap.remove(app.pid); removed = true; } } if (removed) { - mAtmInternal.onProcessUnMapped(pid); + mAtmInternal.onProcessUnMapped(app.pid); } return removed; } @@ -1970,7 +1978,7 @@ public class ActivityManagerService extends IActivityManager.Stub app.getWindowProcessController().setPid(MY_PID); app.maxAdj = ProcessList.SYSTEM_ADJ; app.makeActive(mSystemThread.getApplicationThread(), mProcessStats); - mPidsSelfLocked.put(app.pid, app); + mPidsSelfLocked.put(app); mProcessList.updateLruProcessLocked(app, false, null); updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); } @@ -4601,7 +4609,7 @@ public class ActivityManagerService extends IActivityManager.Stub @GuardedBy("this") private final void processStartTimedOutLocked(ProcessRecord app) { final int pid = app.pid; - boolean gone = mPidsSelfLocked.removeIfNoThread(pid); + boolean gone = mPidsSelfLocked.removeIfNoThread(app); if (gone) { Slog.w(TAG, "Process " + app + " failed to attach"); @@ -4658,6 +4666,26 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (mPidsSelfLocked) { app = mPidsSelfLocked.get(pid); } + if (app != null && (app.startUid != callingUid || app.startSeq != startSeq)) { + String processName = null; + final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq); + if (pending != null) { + processName = pending.processName; + } + final String msg = "attachApplicationLocked process:" + processName + + " startSeq:" + startSeq + + " pid:" + pid + + " belongs to another existing app:" + app.processName + + " startSeq:" + app.startSeq; + Slog.wtf(TAG, msg); + // SafetyNet logging for b/131105245. + EventLog.writeEvent(0x534e4554, "131105245", app.startUid, msg); + // If there is already an app occupying that pid that hasn't been cleaned up + cleanUpApplicationRecordLocked(app, false, false, -1, + true /*replacingPid*/); + mPidsSelfLocked.remove(app); + app = null; + } } else { app = null; } @@ -4666,7 +4694,7 @@ public class ActivityManagerService extends IActivityManager.Stub // update the internal state. if (app == null && startSeq > 0) { final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq); - if (pending != null && pending.startUid == callingUid + if (pending != null && pending.startUid == callingUid && pending.startSeq == startSeq && mProcessList.handleProcessStartedLocked(pending, pid, pending .isUsingWrapper(), startSeq, true)) { @@ -13642,7 +13670,7 @@ public class ActivityManagerService extends IActivityManager.Stub return true; } else if (app.pid > 0 && app.pid != MY_PID) { // Goodbye! - mPidsSelfLocked.remove(app.pid); + mPidsSelfLocked.remove(app); mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid); if (app.isolated) { diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index b394eea95a88..dc94c1e01250 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -1445,10 +1445,11 @@ public final class ProcessList { long startTime = SystemClock.elapsedRealtime(); if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) { checkSlow(startTime, "startProcess: removing from pids map"); - mService.mPidsSelfLocked.remove(app.pid); + mService.mPidsSelfLocked.remove(app); mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); checkSlow(startTime, "startProcess: done removing from pids map"); app.setPid(0); + app.startSeq = 0; } if (DEBUG_PROCESSES && mService.mProcessesOnHold.contains(app)) Slog.v( @@ -1656,6 +1657,14 @@ public final class ProcessList { app.killedByAm = false; app.removed = false; app.killed = false; + if (app.startSeq != 0) { + Slog.wtf(TAG, "startProcessLocked processName:" + app.processName + + " with non-zero startSeq:" + app.startSeq); + } + if (app.pid != 0) { + Slog.wtf(TAG, "startProcessLocked processName:" + app.processName + + " with non-zero pid:" + app.pid); + } final long startSeq = app.startSeq = ++mProcStartSeqCounter; app.setStartParams(uid, hostingRecord, seInfo, startTime); app.setUsingWrapper(invokeWith != null @@ -2063,12 +2072,15 @@ public final class ProcessList { // If there is already an app occupying that pid that hasn't been cleaned up if (oldApp != null && !app.isolated) { // Clean up anything relating to this pid first - Slog.w(TAG, "Reusing pid " + pid - + " while app is still mapped to it"); + Slog.wtf(TAG, "handleProcessStartedLocked process:" + app.processName + + " startSeq:" + app.startSeq + + " pid:" + pid + + " belongs to another existing app:" + oldApp.processName + + " startSeq:" + oldApp.startSeq); mService.cleanUpApplicationRecordLocked(oldApp, false, false, -1, true /*replacingPid*/); } - mService.mPidsSelfLocked.put(pid, app); + mService.mPidsSelfLocked.put(app); synchronized (mService.mPidsSelfLocked) { if (!procAttached) { Message msg = mService.mHandler.obtainMessage(PROC_START_TIMEOUT_MSG); @@ -2241,7 +2253,7 @@ public final class ProcessList { .pendingStart)) { int pid = app.pid; if (pid > 0) { - mService.mPidsSelfLocked.remove(pid); + mService.mPidsSelfLocked.remove(app); mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid); if (app.isolated) { diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index b9a6a1020a77..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) { 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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 4f859412f5f4..13b4ab927c59 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -23,6 +23,7 @@ import static android.app.Notification.FLAG_BUBBLE; import static android.app.Notification.FLAG_FOREGROUND_SERVICE; import static android.app.Notification.FLAG_NO_CLEAR; import static android.app.Notification.FLAG_ONGOING_EVENT; +import static android.app.Notification.FLAG_ONLY_ALERT_ONCE; import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED; import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED; import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED; @@ -1031,12 +1032,19 @@ public class NotificationManagerService extends SystemService { final StatusBarNotification n = r.sbn; final int callingUid = n.getUid(); final String pkg = n.getPackageName(); + final boolean wasBubble = r.getNotification().isBubbleNotification(); if (isBubble && isNotificationAppropriateToBubble(r, pkg, callingUid, null /* oldEntry */)) { r.getNotification().flags |= FLAG_BUBBLE; } else { r.getNotification().flags &= ~FLAG_BUBBLE; } + if (wasBubble != r.getNotification().isBubbleNotification()) { + // Add the "alert only once" flag so that the notification won't HUN + // unnecessarily just because the bubble flag was changed. + r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE; + mListeners.notifyPostedLocked(r, r); + } } } } @@ -5732,7 +5740,7 @@ public class NotificationManagerService extends SystemService { } // Suppressed because it's a silent update final Notification notification = record.getNotification(); - if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) { + if (record.isUpdate && (notification.flags & FLAG_ONLY_ALERT_ONCE) != 0) { return false; } // Suppressed because another notification in its group handles alerting @@ -5751,7 +5759,7 @@ public class NotificationManagerService extends SystemService { boolean shouldMuteNotificationLocked(final NotificationRecord record) { // Suppressed because it's a silent update final Notification notification = record.getNotification(); - if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) { + if (record.isUpdate && (notification.flags & FLAG_ONLY_ALERT_ONCE) != 0) { return true; } diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java index 7ee167adfd38..99f583978535 100644 --- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java +++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java @@ -120,42 +120,65 @@ final class OverlayManagerShellCommand extends ShellCommand { return 1; } } + final String packageName = getNextArg(); + if (packageName != null) { + List<OverlayInfo> overlaysForTarget = mInterface.getOverlayInfosForTarget( + packageName, userId); + + // If the package is not targeted by any overlays, check if the package is an overlay. + if (overlaysForTarget.isEmpty()) { + final OverlayInfo info = mInterface.getOverlayInfo(packageName, userId); + if (info != null) { + printListOverlay(out, info); + } + return 0; + } + + out.println(packageName); + + // Print the overlays for the target. + final int n = overlaysForTarget.size(); + for (int i = 0; i < n; i++) { + printListOverlay(out, overlaysForTarget.get(i)); + } + + return 0; + } + // Print all overlays grouped by target package name. final Map<String, List<OverlayInfo>> allOverlays = mInterface.getAllOverlays(userId); for (final String targetPackageName : allOverlays.keySet()) { - if (targetPackageName.equals(packageName)) { - out.println(targetPackageName); - } + out.println(targetPackageName); + List<OverlayInfo> overlaysForTarget = allOverlays.get(targetPackageName); final int n = overlaysForTarget.size(); for (int i = 0; i < n; i++) { - final OverlayInfo oi = overlaysForTarget.get(i); - if (!targetPackageName.equals(packageName) && !oi.packageName.equals(packageName)) { - continue; - } - String status; - switch (oi.state) { - case OverlayInfo.STATE_ENABLED_STATIC: - case OverlayInfo.STATE_ENABLED: - status = "[x]"; - break; - case OverlayInfo.STATE_DISABLED: - status = "[ ]"; - break; - default: - status = "---"; - break; - } - out.println(String.format("%s %s", status, oi.packageName)); - } - if (targetPackageName.equals(packageName)) { - out.println(); + printListOverlay(out, overlaysForTarget.get(i)); } + out.println(); } + return 0; } + private void printListOverlay(PrintWriter out, OverlayInfo oi) { + String status; + switch (oi.state) { + case OverlayInfo.STATE_ENABLED_STATIC: + case OverlayInfo.STATE_ENABLED: + status = "[x]"; + break; + case OverlayInfo.STATE_DISABLED: + status = "[ ]"; + break; + default: + status = "---"; + break; + } + out.println(String.format("%s %s", status, oi.packageName)); + } + private int runEnableDisable(final boolean enable) throws RemoteException { final PrintWriter err = getErrPrintWriter(); diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java index fe0b9a6acc85..b7e18c35829e 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java @@ -537,6 +537,7 @@ public class BatterySaverStateMachine { Slog.d(TAG, "doAutoBatterySaverLocked: mBootCompleted=" + mBootCompleted + " mSettingsLoaded=" + mSettingsLoaded + " mBatteryStatusSet=" + mBatteryStatusSet + + " mState=" + mState + " mIsBatteryLevelLow=" + mIsBatteryLevelLow + " mIsPowered=" + mIsPowered + " mSettingAutomaticBatterySaver=" + mSettingAutomaticBatterySaver @@ -689,9 +690,9 @@ public class BatterySaverStateMachine { final boolean isStickyDisabled = mBatterySaverStickyBehaviourDisabled || !mSettingBatterySaverEnabledSticky; if (isStickyDisabled || shouldTurnOffSticky) { + mState = STATE_OFF; setStickyActive(false); triggerStickyDisabledNotification(); - mState = STATE_OFF; } else if (!mIsPowered) { // Re-enable BS. enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, @@ -797,7 +798,8 @@ public class BatterySaverStateMachine { Intent.ACTION_POWER_USAGE_SUMMARY)); } - private void triggerStickyDisabledNotification() { + @VisibleForTesting + void triggerStickyDisabledNotification() { NotificationManager manager = mContext.getSystemService(NotificationManager.class); ensureNotificationChannelExists(manager, BATTERY_SAVER_NOTIF_CHANNEL_ID, R.string.battery_saver_notification_channel_name); 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/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 1344727ab36d..66b305ec2dac 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1085,6 +1085,22 @@ final class ActivityRecord extends ConfigurationContainer { if (root == this) { task.setRootProcess(proc); } + // Override the process configuration to match the display where the first activity in + // the process was launched. This can help with compat issues on secondary displays when + // apps use Application to obtain configuration or metrics instead of Activity. + final ActivityDisplay display = getDisplay(); + if (display == null || display.mDisplayId == INVALID_DISPLAY) { + return; + } + if (!proc.hasActivities() && display.mDisplayId != DEFAULT_DISPLAY) { + proc.registerDisplayConfigurationListenerLocked(display); + } else if (display.mDisplayId == DEFAULT_DISPLAY) { + // Once an activity is launched on default display - stop listening for other displays + // configurations to maintain compatibility with previous platform releases. E.g. when + // an activity is launched in a Bubble and then moved to default screen, we should match + // the global device config. + proc.unregisterDisplayConfigurationListenerLocked(); + } } boolean hasProcess() { @@ -3233,7 +3249,7 @@ final class ActivityRecord extends ConfigurationContainer { // Update last reported values. final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration(); - setLastReportedConfiguration(mAtmService.getGlobalConfiguration(), newMergedOverrideConfig); + setLastReportedConfiguration(getProcessGlobalConfiguration(), newMergedOverrideConfig); if (mState == INITIALIZING) { // No need to relaunch or schedule new config for activity that hasn't been launched @@ -3342,6 +3358,14 @@ final class ActivityRecord extends ConfigurationContainer { return true; } + /** Get process configuration, or global config if the process is not set. */ + private Configuration getProcessGlobalConfiguration() { + if (app != null) { + return app.getConfiguration(); + } + return mAtmService.getGlobalConfiguration(); + } + /** * When assessing a configuration change, decide if the changes flags and the new configurations * should cause the Activity to relaunch. @@ -3449,7 +3473,7 @@ final class ActivityRecord extends ConfigurationContainer { mStackSupervisor.activityRelaunchingLocked(this); final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults, pendingNewIntents, configChangeFlags, - new MergedConfiguration(mAtmService.getGlobalConfiguration(), + new MergedConfiguration(getProcessGlobalConfiguration(), getMergedOverrideConfiguration()), preserveWindow); final ActivityLifecycleItem lifecycleItem; diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java index 1cdb49d25dfd..9d08e10c6dea 100644 --- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java @@ -280,13 +280,6 @@ class ActivityStartInterceptor { mActivityOptions = ActivityOptions.makeBasic(); } - ActivityRecord homeActivityRecord = mRootActivityContainer.getDefaultDisplayHomeActivity(); - if (homeActivityRecord != null && homeActivityRecord.getTaskRecord() != null) { - // Showing credential confirmation activity in home task to avoid stopping - // multi-windowed mode after showing the full-screen credential confirmation activity. - mActivityOptions.setLaunchTaskId(homeActivityRecord.getTaskRecord().taskId); - } - final UserInfo parent = mUserManager.getProfileParent(mUserId); mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid); mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 89962a5418c1..772e5e646825 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -39,7 +39,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECOND import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; -import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; import static android.content.pm.ApplicationInfo.FLAG_FACTORY_TEST; import static android.content.pm.ConfigurationInfo.GL_ES_VERSION_UNDEFINED; import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS; @@ -6892,15 +6891,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | - FLAG_ACTIVITY_TASK_ON_HOME); - ActivityOptions activityOptions = options != null + intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + final ActivityOptions activityOptions = options != null ? new ActivityOptions(options) : ActivityOptions.makeBasic(); - final ActivityRecord homeActivity = - mRootActivityContainer.getDefaultDisplayHomeActivity(); - if (homeActivity != null) { - activityOptions.setLaunchTaskId(homeActivity.getTaskRecord().taskId); - } mContext.startActivityAsUser(intent, activityOptions.toBundle(), UserHandle.CURRENT); } finally { diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index ecbecbafd3d5..3d7e50d91b08 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -2782,6 +2782,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree protected void onAnimationFinished() { super.onAnimationFinished(); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#onAnimationFinished"); mTransit = TRANSIT_UNSET; mTransitFlags = 0; mNeedsZBoost = false; @@ -2816,6 +2817,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree scheduleAnimation(); mActivityRecord.onAnimationFinished(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @Override diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 1e826ee96837..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); 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/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index ab5e071f572a..d83869109ff3 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -319,7 +319,8 @@ public class TaskStack extends WindowContainer<Task> implements * Sets the bounds animation target bounds ahead of an animation. This can't currently be done * in onAnimationStart() since that is started on the UiThread. */ - void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds, boolean toFullscreen) { + private void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds, + boolean toFullscreen) { mBoundsAnimatingRequested = true; mBoundsAnimatingToFullscreen = toFullscreen; if (destBounds != null) { @@ -329,7 +330,11 @@ public class TaskStack extends WindowContainer<Task> implements } if (sourceHintBounds != null) { mBoundsAnimationSourceHintBounds.set(sourceHintBounds); - } else { + } else if (!mBoundsAnimating) { + // If the bounds are already animating, we don't want to reset the source hint. This is + // because the source hint is sent when starting the animation from the client that + // requested to enter pip. Other requests can adjust the pip bounds during an animation, + // but could accidentally reset the source hint bounds. mBoundsAnimationSourceHintBounds.setEmpty(); } diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 12b62b99b3e2..5fc399719aed 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -439,7 +439,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio @Override protected ConfigurationContainer getParent() { - return null; + // Returning RootActivityContainer as the parent, so that this process controller always + // has full configuration and overrides (e.g. from display) are always added on top of + // global config. + return mAtm.mRootActivityContainer; } @HotPath(caller = HotPath.PROCESS_CHANGE) diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 5ef184adc52f..c35e86645719 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3104,7 +3104,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this + ": " + mWindowFrames.mCompatFrame); final MergedConfiguration mergedConfiguration = - new MergedConfiguration(mWmService.mRoot.getConfiguration(), + new MergedConfiguration(getProcessGlobalConfiguration(), getMergedOverrideConfiguration()); setLastReportedMergedConfiguration(mergedConfiguration); 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 2b5cd010705c..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)) { diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java index 6517303842ed..77e2517d7752 100644 --- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java @@ -43,6 +43,8 @@ import static com.android.server.AlarmManagerService.Constants.KEY_LISTENER_TIME import static com.android.server.AlarmManagerService.Constants.KEY_MAX_INTERVAL; import static com.android.server.AlarmManagerService.Constants.KEY_MIN_FUTURITY; import static com.android.server.AlarmManagerService.Constants.KEY_MIN_INTERVAL; +import static com.android.server.AlarmManagerService.IS_WAKEUP_MASK; +import static com.android.server.AlarmManagerService.TIME_CHANGED_MASK; import static com.android.server.AlarmManagerService.WORKING_INDEX; import static org.junit.Assert.assertEquals; @@ -126,13 +128,15 @@ public class AlarmManagerServiceTest { private MockitoSession mMockingSession; private Injector mInjector; private volatile long mNowElapsedTest; + private volatile long mNowRtcTest; @GuardedBy("mTestTimer") private TestTimer mTestTimer = new TestTimer(); static class TestTimer { private long mElapsed; boolean mExpired; - int mType; + private int mType; + private int mFlags; // Flags used to decide what needs to be evaluated. synchronized long getElapsed() { return mElapsed; @@ -147,7 +151,16 @@ public class AlarmManagerServiceTest { return mType; } + synchronized int getFlags() { + return mFlags; + } + synchronized void expire() throws InterruptedException { + expire(IS_WAKEUP_MASK); // Default: evaluate eligibility of all alarms + } + + synchronized void expire(int flags) throws InterruptedException { + mFlags = flags; mExpired = true; notifyAll(); // Now wait for the alarm thread to finish execution. @@ -181,7 +194,7 @@ public class AlarmManagerServiceTest { } mTestTimer.mExpired = false; } - return AlarmManagerService.IS_WAKEUP_MASK; // Doesn't matter, just evaluate. + return mTestTimer.getFlags(); } @Override @@ -215,6 +228,11 @@ public class AlarmManagerServiceTest { } @Override + long getCurrentTimeMillis() { + return mNowRtcTest; + } + + @Override AlarmManagerService.ClockReceiver getClockReceiver(AlarmManagerService service) { return mClockReceiver; } @@ -340,7 +358,7 @@ public class AlarmManagerServiceTest { } @Test - public void testSingleAlarmSet() { + public void singleElapsedAlarmSet() { final long triggerTime = mNowElapsedTest + 5000; final PendingIntent alarmPi = getNewMockPendingIntent(); setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi); @@ -348,6 +366,33 @@ public class AlarmManagerServiceTest { } @Test + public void singleRtcAlarmSet() { + mNowElapsedTest = 54; + mNowRtcTest = 1243; // arbitrary values of time + final long triggerRtc = mNowRtcTest + 5000; + final PendingIntent alarmPi = getNewMockPendingIntent(); + setTestAlarm(RTC_WAKEUP, triggerRtc, alarmPi); + final long triggerElapsed = triggerRtc - (mNowRtcTest - mNowElapsedTest); + assertEquals(triggerElapsed, mTestTimer.getElapsed()); + } + + @Test + public void timeChangeMovesRtcAlarm() throws Exception { + mNowElapsedTest = 42; + mNowRtcTest = 4123; // arbitrary values of time + final long triggerRtc = mNowRtcTest + 5000; + final PendingIntent alarmPi = getNewMockPendingIntent(); + setTestAlarm(RTC_WAKEUP, triggerRtc, alarmPi); + final long triggerElapsed1 = mTestTimer.getElapsed(); + final long timeDelta = -123; + mNowRtcTest += timeDelta; + mTestTimer.expire(TIME_CHANGED_MASK); + final long triggerElapsed2 = mTestTimer.getElapsed(); + assertEquals("Invalid movement of triggerElapsed following time change", triggerElapsed2, + triggerElapsed1 - timeDelta); + } + + @Test public void testSingleAlarmExpiration() throws Exception { final long triggerTime = mNowElapsedTest + 5000; final PendingIntent alarmPi = getNewMockPendingIntent(); diff --git a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java index 212d2a845254..a8faa54fe9d6 100644 --- a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java @@ -15,6 +15,10 @@ */ package com.android.server.power.batterysaver; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -22,6 +26,8 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.NotificationManager; @@ -37,6 +43,7 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InOrder; import java.util.HashMap; import java.util.Objects; @@ -201,6 +208,8 @@ public class BatterySaverStateMachineTest { mDevice = new Device(); mTarget = new TestableBatterySaverStateMachine(); + spyOn(mTarget); + doNothing().when(mTarget).triggerStickyDisabledNotification(); mDevice.pushBatteryStatus(); mTarget.onBootCompleted(); @@ -423,7 +432,7 @@ public class BatterySaverStateMachineTest { assertEquals(70, mPersistedState.batteryLevel); assertEquals(false, mPersistedState.batteryLow); - // Bump ump the threshold. + // Bump up the threshold. mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 70); mDevice.setBatteryLevel(mPersistedState.batteryLevel); @@ -545,6 +554,8 @@ public class BatterySaverStateMachineTest { @Test public void testAutoBatterySaver_withSticky_withAutoOffEnabled() { + InOrder inOrder = inOrder(mTarget); + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50); mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 1); mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, 90); @@ -569,6 +580,7 @@ public class BatterySaverStateMachineTest { assertEquals(true, mDevice.batterySaverEnabled); // Stays on. assertEquals(95, mPersistedState.batteryLevel); assertEquals(false, mPersistedState.batteryLow); + inOrder.verify(mTarget, never()).triggerStickyDisabledNotification(); // Scenario 2: User turns BS on manually above the threshold then charges device. BS // shouldn't turn back on. @@ -584,6 +596,7 @@ public class BatterySaverStateMachineTest { assertEquals(false, mDevice.batterySaverEnabled); // Sticky BS no longer enabled. assertEquals(97, mPersistedState.batteryLevel); assertEquals(false, mPersistedState.batteryLow); + inOrder.verify(mTarget).triggerStickyDisabledNotification(); // Scenario 3: User turns BS on manually above the threshold. Device drains below // threshold and then charged to below threshold. Sticky BS should activate. @@ -612,6 +625,7 @@ public class BatterySaverStateMachineTest { assertEquals(true, mDevice.batterySaverEnabled); assertEquals(30, mPersistedState.batteryLevel); assertEquals(true, mPersistedState.batteryLow); + inOrder.verify(mTarget, never()).triggerStickyDisabledNotification(); // Scenario 4: User turns BS on manually above the threshold. Device drains below // threshold and is eventually charged to above threshold. Sticky BS should turn off. @@ -627,6 +641,7 @@ public class BatterySaverStateMachineTest { assertEquals(false, mDevice.batterySaverEnabled); // Sticky BS no longer enabled. assertEquals(90, mPersistedState.batteryLevel); assertEquals(false, mPersistedState.batteryLow); + inOrder.verify(mTarget).triggerStickyDisabledNotification(); // Scenario 5: User turns BS on manually below threshold and charges to below threshold. // Sticky BS should activate. @@ -654,6 +669,7 @@ public class BatterySaverStateMachineTest { assertEquals(true, mDevice.batterySaverEnabled); // Sticky BS still on. assertEquals(80, mPersistedState.batteryLevel); assertEquals(false, mPersistedState.batteryLow); + inOrder.verify(mTarget, never()).triggerStickyDisabledNotification(); // Scenario 6: User turns BS on manually below threshold and eventually charges to above // threshold. Sticky BS should turn off. @@ -665,6 +681,7 @@ public class BatterySaverStateMachineTest { assertEquals(false, mDevice.batterySaverEnabled); assertEquals(95, mPersistedState.batteryLevel); assertEquals(false, mPersistedState.batteryLow); + inOrder.verify(mTarget).triggerStickyDisabledNotification(); // Scenario 7: User turns BS on above threshold and then reboots device. Sticky BS // shouldn't activate. @@ -676,6 +693,8 @@ public class BatterySaverStateMachineTest { assertEquals(false, mDevice.batterySaverEnabled); assertEquals(93, mPersistedState.batteryLevel); assertEquals(false, mPersistedState.batteryLow); + // initDevice() changes the mTarget reference, so inOrder is invalid here. + verify(mTarget).triggerStickyDisabledNotification(); // Scenario 8: User turns BS on below threshold and then reboots device without charging. // Sticky BS should activate. @@ -690,6 +709,8 @@ public class BatterySaverStateMachineTest { assertEquals(true, mDevice.batterySaverEnabled); assertEquals(75, mPersistedState.batteryLevel); assertEquals(false, mPersistedState.batteryLow); + // initDevice() changes the mTarget reference, so inOrder is invalid here. + verify(mTarget, never()).triggerStickyDisabledNotification(); // Scenario 9: User turns BS on below threshold and then reboots device after charging // above threshold. Sticky BS shouldn't activate. @@ -702,6 +723,8 @@ public class BatterySaverStateMachineTest { assertEquals(false, mDevice.batterySaverEnabled); assertEquals(100, mPersistedState.batteryLevel); assertEquals(false, mPersistedState.batteryLow); + // initDevice() changes the mTarget reference, so inOrder is invalid here. + verify(mTarget).triggerStickyDisabledNotification(); // Scenario 10: Somehow autoDisableLevel is set to a value below lowPowerModeTriggerLevel // and then user enables manually above both thresholds, discharges below @@ -738,6 +761,8 @@ public class BatterySaverStateMachineTest { assertEquals(true, mDevice.batterySaverEnabled); assertEquals(65, mPersistedState.batteryLevel); assertEquals(true, mPersistedState.batteryLow); + // initDevice() changes the mTarget reference, so inOrder is invalid here. + verify(mTarget, never()).triggerStickyDisabledNotification(); } @Test @@ -780,6 +805,7 @@ public class BatterySaverStateMachineTest { assertEquals(false, mDevice.batterySaverEnabled); // Sticky BS no longer enabled. assertEquals(95, mPersistedState.batteryLevel); assertEquals(false, mPersistedState.batteryLow); + verify(mTarget).triggerStickyDisabledNotification(); } @Test 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 19ed5f349abe..f85e2cc10800 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java @@ -163,5 +163,4 @@ public class LockSettingsServiceTestable extends LockSettingsService { } return storedData; } - } 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/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index d2332bf86e95..3661e89a5d00 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -22,6 +22,7 @@ import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIB import static android.app.Notification.CATEGORY_CALL; import static android.app.Notification.FLAG_BUBBLE; import static android.app.Notification.FLAG_FOREGROUND_SERVICE; +import static android.app.Notification.FLAG_ONLY_ALERT_ONCE; import static android.app.NotificationManager.EXTRA_BLOCKED_STATE; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; @@ -119,7 +120,6 @@ import android.provider.Settings; import android.service.notification.Adjustment; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationStats; -import android.service.notification.NotifyingApp; import android.service.notification.StatusBarNotification; import android.service.notification.ZenPolicy; import android.test.suitebuilder.annotation.SmallTest; @@ -165,10 +165,8 @@ import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.function.Consumer; @SmallTest @@ -5012,6 +5010,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); waitForIdle(); + // Reset as this is called when the notif is first sent + reset(mListeners); + // First we were a bubble StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG); assertEquals(1, notifsBefore.length); @@ -5021,10 +5022,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), false); waitForIdle(); - // Now we are not a bubble - StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG); - assertEquals(1, notifsAfter.length); - assertEquals((notifsAfter[0].getNotification().flags & FLAG_BUBBLE), 0); + // Make sure we are not a bubble / reported as such to listeners + ArgumentCaptor<NotificationRecord> captor = + ArgumentCaptor.forClass(NotificationRecord.class); + verify(mListeners, times(1)).notifyPostedLocked(captor.capture(), any()); + + assertEquals((captor.getValue().getNotification().flags & FLAG_BUBBLE), 0); + assertTrue((captor.getValue().getNotification().flags & FLAG_ONLY_ALERT_ONCE) != 0); } @Test @@ -5054,14 +5058,20 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn( IMPORTANCE_FOREGROUND); + // Reset as this is called when the notif is first sent + reset(mListeners); + // Notify we are now a bubble mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), true); waitForIdle(); - // Make sure we are a bubble - StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG); - assertEquals(1, notifsAfter.length); - assertTrue((notifsAfter[0].getNotification().flags & FLAG_BUBBLE) != 0); + // Make sure we are a bubble / reported as such to listeners + ArgumentCaptor<NotificationRecord> captor = + ArgumentCaptor.forClass(NotificationRecord.class); + verify(mListeners, times(1)).notifyPostedLocked(captor.capture(), any()); + + assertTrue((captor.getValue().getNotification().flags & FLAG_BUBBLE) != 0); + assertTrue((captor.getValue().getNotification().flags & FLAG_ONLY_ALERT_ONCE) != 0); } @Test @@ -5082,6 +5092,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); waitForIdle(); + // Reset as this is called when the notif is first sent + reset(mListeners); + // Would be a normal notification because wouldn't have met requirements to bubble StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG); assertEquals(1, notifsBefore.length); @@ -5095,6 +5108,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG); assertEquals(1, notifsAfter.length); assertEquals((notifsAfter[0].getNotification().flags & FLAG_BUBBLE), 0); + verify(mListeners, times(0)).notifyPostedLocked(any(), any()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 11a177a71a10..f8fd64a8feb2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_90; @@ -38,6 +40,7 @@ import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING; import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE; import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE; import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; +import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -56,10 +59,10 @@ import android.app.servertransaction.PauseActivityItem; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; -import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.util.MergedConfiguration; import android.util.MutableBoolean; +import android.view.DisplayInfo; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner.Stub; import android.view.RemoteAnimationAdapter; @@ -598,6 +601,67 @@ public class ActivityRecordTests extends ActivityTestsBase { assertNull(mActivity.pendingOptions); } + @Test + public void testSetProcessOverridesConfig() { + final ActivityRecord defaultDisplayActivity = + createActivityOnDisplay(true /* defaultDisplay */, null /* process */); + assertFalse(defaultDisplayActivity.app.registeredForDisplayConfigChanges()); + + final ActivityRecord secondaryDisplayActivity = + createActivityOnDisplay(false /* defaultDisplay */, null /* process */); + assertTrue(secondaryDisplayActivity.app.registeredForDisplayConfigChanges()); + assertEquals(secondaryDisplayActivity.getDisplay().getResolvedOverrideConfiguration(), + secondaryDisplayActivity.app.getRequestedOverrideConfiguration()); + + assertNotEquals(defaultDisplayActivity.getConfiguration(), + secondaryDisplayActivity.getConfiguration()); + } + + @Test + public void testSetProcessDoesntOverrideConfigIfAnotherActivityPresent() { + final ActivityRecord defaultDisplayActivity = + createActivityOnDisplay(true /* defaultDisplay */, null /* process */); + assertFalse(defaultDisplayActivity.app.registeredForDisplayConfigChanges()); + + final ActivityRecord secondaryDisplayActivity = + createActivityOnDisplay(false /* defaultDisplay */, defaultDisplayActivity.app); + assertFalse(secondaryDisplayActivity.app.registeredForDisplayConfigChanges()); + } + + @Test + public void testActivityOnDefaultDisplayClearsProcessOverride() { + final ActivityRecord secondaryDisplayActivity = + createActivityOnDisplay(false /* defaultDisplay */, null /* process */); + assertTrue(secondaryDisplayActivity.app.registeredForDisplayConfigChanges()); + + final ActivityRecord defaultDisplayActivity = + createActivityOnDisplay(true /* defaultDisplay */, + secondaryDisplayActivity.app); + assertFalse(defaultDisplayActivity.app.registeredForDisplayConfigChanges()); + assertFalse(secondaryDisplayActivity.app.registeredForDisplayConfigChanges()); + } + + /** + * Creates an activity on display. For non-default display request it will also create a new + * display with custom DisplayInfo. + */ + private ActivityRecord createActivityOnDisplay(boolean defaultDisplay, + WindowProcessController process) { + final ActivityDisplay display; + if (defaultDisplay) { + display = mRootActivityContainer.getDefaultDisplay(); + } else { + final DisplayInfo info = new DisplayInfo(); + info.logicalWidth = 100; + info.logicalHeight = 100; + display = addNewActivityDisplayAt(info, POSITION_TOP); + } + final TestActivityStack stack = display.createStack(WINDOWING_MODE_UNDEFINED, + ACTIVITY_TYPE_STANDARD, true /* onTop */); + final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build(); + return new ActivityBuilder(mService).setTask(task).setUseProcess(process).build(); + } + /** Setup {@link #mActivity} as a size-compat-mode-able activity without fixed orientation. */ private void prepareFixedAspectRatioUnresizableActivity() { setupDisplayContentForCompatDisplayInsets(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index 53b0add8c37e..d8c0de741845 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -195,6 +195,7 @@ class ActivityTestsBase { private ActivityStack mStack; private int mActivityFlags; private int mLaunchMode; + private WindowProcessController mWpc; ActivityBuilder(ActivityTaskManagerService service) { mService = service; @@ -245,6 +246,11 @@ class ActivityTestsBase { return this; } + ActivityBuilder setUseProcess(WindowProcessController wpc) { + mWpc = wpc; + return this; + } + ActivityRecord build() { if (mComponent == null) { final int id = sCurrentActivityId++; @@ -290,12 +296,18 @@ class ActivityTestsBase { mTaskRecord.addActivityToTop(activity); } - final WindowProcessController wpc = new WindowProcessController(mService, - mService.mContext.getApplicationInfo(), "name", 12345, - UserHandle.getUserId(12345), mock(Object.class), - mock(WindowProcessListener.class)); - wpc.setThread(mock(IApplicationThread.class)); + final WindowProcessController wpc; + if (mWpc != null) { + wpc = mWpc; + } else { + wpc = new WindowProcessController(mService, + mService.mContext.getApplicationInfo(), "name", 12345, + UserHandle.getUserId(12345), mock(Object.class), + mock(WindowProcessListener.class)); + wpc.setThread(mock(IApplicationThread.class)); + } activity.setProcess(wpc); + wpc.addActivityIfNeeded(activity); return activity; } } 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..8528954a6b5b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -153,7 +153,6 @@ public class AppWindowTokenTests extends WindowTestsBase { @FlakyTest(bugId = 131005232) public void testLandscapeSeascapeRotationByApp() { // Some plumbing to get the service ready for rotation updates. - mWm.mDisplayReady = true; mWm.mDisplayEnabled = true; final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( @@ -186,7 +185,6 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testLandscapeSeascapeRotationByPolicy() { // Some plumbing to get the service ready for rotation updates. - mWm.mDisplayReady = true; mWm.mDisplayEnabled = true; final DisplayRotation spiedRotation = spy(mDisplayContent.getDisplayRotation()); @@ -379,6 +377,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/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index 366aceafd7bf..427a92963807 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -41,6 +41,7 @@ import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.IntentFilter; +import android.content.res.Configuration; import android.database.ContentObserver; import android.hardware.display.DisplayManagerInternal; import android.net.Uri; @@ -175,6 +176,12 @@ public class SystemServicesTestRule implements TestRule { // Display creation is driven by the ActivityManagerService via // ActivityStackSupervisor. We emulate those steps here. mWindowManagerService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class)); + mWindowManagerService.displayReady(); + + final Configuration defaultDisplayConfig = + mWindowManagerService.computeNewConfiguration(DEFAULT_DISPLAY); + doReturn(defaultDisplayConfig).when(atms).getGlobalConfiguration(); + doReturn(defaultDisplayConfig).when(atms).getGlobalConfigurationForPid(anyInt()); mMockTracker.stopTracking(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index a7c84a1c28b4..7b8fba03b6d7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -26,7 +26,9 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import android.content.pm.ApplicationInfo; +import android.content.res.Configuration; import android.platform.test.annotations.Presubmit; +import android.view.DisplayInfo; import org.junit.Test; @@ -78,6 +80,26 @@ public class WindowProcessControllerTests extends ActivityTestsBase { assertEquals(INVALID_DISPLAY, wpc.getDisplayId()); } + @Test + public void testConfigurationForSecondaryScreen() { + final WindowProcessController wpc = new WindowProcessController( + mService, mock(ApplicationInfo.class), null, 0, -1, null, null); + //By default, the process should not listen to any display. + assertEquals(INVALID_DISPLAY, wpc.getDisplayId()); + + // Register to a new display as a listener. + final DisplayInfo info = new DisplayInfo(); + info.logicalWidth = 100; + info.logicalHeight = 100; + TestActivityDisplay display = addNewActivityDisplayAt(info, WindowContainer.POSITION_TOP); + wpc.registerDisplayConfigurationListenerLocked(display); + + assertEquals(display.mDisplayId, wpc.getDisplayId()); + final Configuration expectedConfig = mService.mRootActivityContainer.getConfiguration(); + expectedConfig.updateFrom(display.getConfiguration()); + assertEquals(expectedConfig, wpc.getConfiguration()); + } + private TestActivityDisplay createTestActivityDisplayInContainer() { final TestActivityDisplay testActivityDisplay = createNewActivityDisplay(); mRootActivityContainer.addChild(testActivityDisplay, POSITION_TOP); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 3a702cb9521c..de28b5f0fa4f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -154,7 +154,6 @@ class WindowTestsBase { context.getDisplay().getDisplayInfo(mDisplayInfo); mDisplayContent = createNewDisplay(); mWm.mDisplayEnabled = true; - mWm.mDisplayReady = true; // Set-up some common windows. mCommonWindows = new HashSet<>(); diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index eefaf47d0387..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 diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java index 14ae68981745..9d732baf7514 100644 --- a/telephony/java/android/telephony/CellSignalStrengthGsm.java +++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java @@ -18,6 +18,7 @@ package android.telephony; import android.annotation.IntRange; import android.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; @@ -46,7 +47,7 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P private int mRssi; // in dBm [-113, -51] or UNAVAILABLE @UnsupportedAppUsage private int mBitErrorRate; // bit error rate (0-7, 99) TS 27.007 8.5 or UNAVAILABLE - @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O) + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private int mTimingAdvance; // range from 0-219 or CellInfo.UNAVAILABLE if unknown private int mLevel; diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 1551ce35d2c4..984d8f705a65 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -337,7 +337,7 @@ public class ServiceState implements Parcelable { * Reference: 3GPP TS 36.104 5.4.3 */ private int mLteEarfcnRsrpBoost = 0; - private List<NetworkRegistrationInfo> mNetworkRegistrationInfos = new ArrayList<>(); + private final List<NetworkRegistrationInfo> mNetworkRegistrationInfos = new ArrayList<>(); private String mOperatorAlphaLongRaw; private String mOperatorAlphaShortRaw; @@ -420,8 +420,10 @@ public class ServiceState implements Parcelable { mCellBandwidths = s.mCellBandwidths == null ? null : Arrays.copyOf(s.mCellBandwidths, s.mCellBandwidths.length); mLteEarfcnRsrpBoost = s.mLteEarfcnRsrpBoost; - mNetworkRegistrationInfos = s.mNetworkRegistrationInfos == null ? null : - s.getNetworkRegistrationInfoList(); + synchronized (mNetworkRegistrationInfos) { + mNetworkRegistrationInfos.clear(); + mNetworkRegistrationInfos.addAll(s.getNetworkRegistrationInfoList()); + } mNrFrequencyRange = s.mNrFrequencyRange; mOperatorAlphaLongRaw = s.mOperatorAlphaLongRaw; mOperatorAlphaShortRaw = s.mOperatorAlphaShortRaw; @@ -453,8 +455,9 @@ public class ServiceState implements Parcelable { mCdmaEriIconMode = in.readInt(); mIsEmergencyOnly = in.readInt() != 0; mLteEarfcnRsrpBoost = in.readInt(); - mNetworkRegistrationInfos = new ArrayList<>(); - in.readList(mNetworkRegistrationInfos, NetworkRegistrationInfo.class.getClassLoader()); + synchronized (mNetworkRegistrationInfos) { + in.readList(mNetworkRegistrationInfos, NetworkRegistrationInfo.class.getClassLoader()); + } mChannelNumber = in.readInt(); mCellBandwidths = in.createIntArray(); mNrFrequencyRange = in.readInt(); @@ -481,7 +484,9 @@ public class ServiceState implements Parcelable { out.writeInt(mCdmaEriIconMode); out.writeInt(mIsEmergencyOnly ? 1 : 0); out.writeInt(mLteEarfcnRsrpBoost); - out.writeList(mNetworkRegistrationInfos); + synchronized (mNetworkRegistrationInfos) { + out.writeList(mNetworkRegistrationInfos); + } out.writeInt(mChannelNumber); out.writeIntArray(mCellBandwidths); out.writeInt(mNrFrequencyRange); @@ -823,31 +828,33 @@ public class ServiceState implements Parcelable { @Override public int hashCode() { - return Objects.hash( - mVoiceRegState, - mDataRegState, - mChannelNumber, - Arrays.hashCode(mCellBandwidths), - mVoiceOperatorAlphaLong, - mVoiceOperatorAlphaShort, - mVoiceOperatorNumeric, - mDataOperatorAlphaLong, - mDataOperatorAlphaShort, - mDataOperatorNumeric, - mIsManualNetworkSelection, - mCssIndicator, - mNetworkId, - mSystemId, - mCdmaRoamingIndicator, - mCdmaDefaultRoamingIndicator, - mCdmaEriIconIndex, - mCdmaEriIconMode, - mIsEmergencyOnly, - mLteEarfcnRsrpBoost, - mNetworkRegistrationInfos, - mNrFrequencyRange, - mOperatorAlphaLongRaw, - mOperatorAlphaShortRaw); + synchronized (mNetworkRegistrationInfos) { + return Objects.hash( + mVoiceRegState, + mDataRegState, + mChannelNumber, + Arrays.hashCode(mCellBandwidths), + mVoiceOperatorAlphaLong, + mVoiceOperatorAlphaShort, + mVoiceOperatorNumeric, + mDataOperatorAlphaLong, + mDataOperatorAlphaShort, + mDataOperatorNumeric, + mIsManualNetworkSelection, + mCssIndicator, + mNetworkId, + mSystemId, + mCdmaRoamingIndicator, + mCdmaDefaultRoamingIndicator, + mCdmaEriIconIndex, + mCdmaEriIconMode, + mIsEmergencyOnly, + mLteEarfcnRsrpBoost, + mNetworkRegistrationInfos, + mNrFrequencyRange, + mOperatorAlphaLongRaw, + mOperatorAlphaShortRaw); + } } @Override @@ -855,30 +862,31 @@ public class ServiceState implements Parcelable { if (!(o instanceof ServiceState)) return false; ServiceState s = (ServiceState) o; - return mVoiceRegState == s.mVoiceRegState - && mDataRegState == s.mDataRegState - && mIsManualNetworkSelection == s.mIsManualNetworkSelection - && mChannelNumber == s.mChannelNumber - && Arrays.equals(mCellBandwidths, s.mCellBandwidths) - && equalsHandlesNulls(mVoiceOperatorAlphaLong, s.mVoiceOperatorAlphaLong) - && equalsHandlesNulls(mVoiceOperatorAlphaShort, s.mVoiceOperatorAlphaShort) - && equalsHandlesNulls(mVoiceOperatorNumeric, s.mVoiceOperatorNumeric) - && equalsHandlesNulls(mDataOperatorAlphaLong, s.mDataOperatorAlphaLong) - && equalsHandlesNulls(mDataOperatorAlphaShort, s.mDataOperatorAlphaShort) - && equalsHandlesNulls(mDataOperatorNumeric, s.mDataOperatorNumeric) - && equalsHandlesNulls(mCssIndicator, s.mCssIndicator) - && equalsHandlesNulls(mNetworkId, s.mNetworkId) - && equalsHandlesNulls(mSystemId, s.mSystemId) - && equalsHandlesNulls(mCdmaRoamingIndicator, s.mCdmaRoamingIndicator) - && equalsHandlesNulls(mCdmaDefaultRoamingIndicator, - s.mCdmaDefaultRoamingIndicator) - && mIsEmergencyOnly == s.mIsEmergencyOnly - && equalsHandlesNulls(mOperatorAlphaLongRaw, s.mOperatorAlphaLongRaw) - && equalsHandlesNulls(mOperatorAlphaShortRaw, s.mOperatorAlphaShortRaw) - && (mNetworkRegistrationInfos == null - ? s.mNetworkRegistrationInfos == null : s.mNetworkRegistrationInfos != null - && mNetworkRegistrationInfos.containsAll(s.mNetworkRegistrationInfos)) - && mNrFrequencyRange == s.mNrFrequencyRange; + synchronized (mNetworkRegistrationInfos) { + return mVoiceRegState == s.mVoiceRegState + && mDataRegState == s.mDataRegState + && mIsManualNetworkSelection == s.mIsManualNetworkSelection + && mChannelNumber == s.mChannelNumber + && Arrays.equals(mCellBandwidths, s.mCellBandwidths) + && equalsHandlesNulls(mVoiceOperatorAlphaLong, s.mVoiceOperatorAlphaLong) + && equalsHandlesNulls(mVoiceOperatorAlphaShort, s.mVoiceOperatorAlphaShort) + && equalsHandlesNulls(mVoiceOperatorNumeric, s.mVoiceOperatorNumeric) + && equalsHandlesNulls(mDataOperatorAlphaLong, s.mDataOperatorAlphaLong) + && equalsHandlesNulls(mDataOperatorAlphaShort, s.mDataOperatorAlphaShort) + && equalsHandlesNulls(mDataOperatorNumeric, s.mDataOperatorNumeric) + && equalsHandlesNulls(mCssIndicator, s.mCssIndicator) + && equalsHandlesNulls(mNetworkId, s.mNetworkId) + && equalsHandlesNulls(mSystemId, s.mSystemId) + && equalsHandlesNulls(mCdmaRoamingIndicator, s.mCdmaRoamingIndicator) + && equalsHandlesNulls(mCdmaDefaultRoamingIndicator, + s.mCdmaDefaultRoamingIndicator) + && mIsEmergencyOnly == s.mIsEmergencyOnly + && equalsHandlesNulls(mOperatorAlphaLongRaw, s.mOperatorAlphaLongRaw) + && equalsHandlesNulls(mOperatorAlphaShortRaw, s.mOperatorAlphaShortRaw) + && mNetworkRegistrationInfos.size() == s.mNetworkRegistrationInfos.size() + && mNetworkRegistrationInfos.containsAll(s.mNetworkRegistrationInfos) + && mNrFrequencyRange == s.mNrFrequencyRange; + } } /** @@ -1005,36 +1013,38 @@ public class ServiceState implements Parcelable { @Override public String toString() { - return new StringBuilder().append("{mVoiceRegState=").append(mVoiceRegState) - .append("(" + rilServiceStateToString(mVoiceRegState) + ")") - .append(", mDataRegState=").append(mDataRegState) - .append("(" + rilServiceStateToString(mDataRegState) + ")") - .append(", mChannelNumber=").append(mChannelNumber) - .append(", duplexMode()=").append(getDuplexMode()) - .append(", mCellBandwidths=").append(Arrays.toString(mCellBandwidths)) - .append(", mVoiceOperatorAlphaLong=").append(mVoiceOperatorAlphaLong) - .append(", mVoiceOperatorAlphaShort=").append(mVoiceOperatorAlphaShort) - .append(", mDataOperatorAlphaLong=").append(mDataOperatorAlphaLong) - .append(", mDataOperatorAlphaShort=").append(mDataOperatorAlphaShort) - .append(", isManualNetworkSelection=").append(mIsManualNetworkSelection) - .append(mIsManualNetworkSelection ? "(manual)" : "(automatic)") - .append(", getRilVoiceRadioTechnology=").append(getRilVoiceRadioTechnology()) - .append("(" + rilRadioTechnologyToString(getRilVoiceRadioTechnology()) + ")") - .append(", getRilDataRadioTechnology=").append(getRilDataRadioTechnology()) - .append("(" + rilRadioTechnologyToString(getRilDataRadioTechnology()) + ")") - .append(", mCssIndicator=").append(mCssIndicator ? "supported" : "unsupported") - .append(", mNetworkId=").append(mNetworkId) - .append(", mSystemId=").append(mSystemId) - .append(", mCdmaRoamingIndicator=").append(mCdmaRoamingIndicator) - .append(", mCdmaDefaultRoamingIndicator=").append(mCdmaDefaultRoamingIndicator) - .append(", mIsEmergencyOnly=").append(mIsEmergencyOnly) - .append(", isUsingCarrierAggregation=").append(isUsingCarrierAggregation()) - .append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost) - .append(", mNetworkRegistrationInfos=").append(mNetworkRegistrationInfos) - .append(", mNrFrequencyRange=").append(mNrFrequencyRange) - .append(", mOperatorAlphaLongRaw=").append(mOperatorAlphaLongRaw) - .append(", mOperatorAlphaShortRaw=").append(mOperatorAlphaShortRaw) - .append("}").toString(); + synchronized (mNetworkRegistrationInfos) { + return new StringBuilder().append("{mVoiceRegState=").append(mVoiceRegState) + .append("(" + rilServiceStateToString(mVoiceRegState) + ")") + .append(", mDataRegState=").append(mDataRegState) + .append("(" + rilServiceStateToString(mDataRegState) + ")") + .append(", mChannelNumber=").append(mChannelNumber) + .append(", duplexMode()=").append(getDuplexMode()) + .append(", mCellBandwidths=").append(Arrays.toString(mCellBandwidths)) + .append(", mVoiceOperatorAlphaLong=").append(mVoiceOperatorAlphaLong) + .append(", mVoiceOperatorAlphaShort=").append(mVoiceOperatorAlphaShort) + .append(", mDataOperatorAlphaLong=").append(mDataOperatorAlphaLong) + .append(", mDataOperatorAlphaShort=").append(mDataOperatorAlphaShort) + .append(", isManualNetworkSelection=").append(mIsManualNetworkSelection) + .append(mIsManualNetworkSelection ? "(manual)" : "(automatic)") + .append(", getRilVoiceRadioTechnology=").append(getRilVoiceRadioTechnology()) + .append("(" + rilRadioTechnologyToString(getRilVoiceRadioTechnology()) + ")") + .append(", getRilDataRadioTechnology=").append(getRilDataRadioTechnology()) + .append("(" + rilRadioTechnologyToString(getRilDataRadioTechnology()) + ")") + .append(", mCssIndicator=").append(mCssIndicator ? "supported" : "unsupported") + .append(", mNetworkId=").append(mNetworkId) + .append(", mSystemId=").append(mSystemId) + .append(", mCdmaRoamingIndicator=").append(mCdmaRoamingIndicator) + .append(", mCdmaDefaultRoamingIndicator=").append(mCdmaDefaultRoamingIndicator) + .append(", mIsEmergencyOnly=").append(mIsEmergencyOnly) + .append(", isUsingCarrierAggregation=").append(isUsingCarrierAggregation()) + .append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost) + .append(", mNetworkRegistrationInfos=").append(mNetworkRegistrationInfos) + .append(", mNrFrequencyRange=").append(mNrFrequencyRange) + .append(", mOperatorAlphaLongRaw=").append(mOperatorAlphaLongRaw) + .append(", mOperatorAlphaShortRaw=").append(mOperatorAlphaShortRaw) + .append("}").toString(); + } } private void init() { @@ -1060,17 +1070,19 @@ public class ServiceState implements Parcelable { mIsEmergencyOnly = false; mLteEarfcnRsrpBoost = 0; mNrFrequencyRange = FREQUENCY_RANGE_UNKNOWN; - mNetworkRegistrationInfos.clear(); - addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder() - .setDomain(NetworkRegistrationInfo.DOMAIN_CS) - .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) - .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN) - .build()); - addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder() - .setDomain(NetworkRegistrationInfo.DOMAIN_PS) - .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) - .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN) - .build()); + synchronized (mNetworkRegistrationInfos) { + mNetworkRegistrationInfos.clear(); + addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder() + .setDomain(NetworkRegistrationInfo.DOMAIN_CS) + .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) + .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN) + .build()); + addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder() + .setDomain(NetworkRegistrationInfo.DOMAIN_PS) + .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) + .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN) + .build()); + } mOperatorAlphaLongRaw = null; mOperatorAlphaShortRaw = null; } @@ -1913,10 +1925,13 @@ public class ServiceState implements Parcelable { */ public ServiceState sanitizeLocationInfo(boolean removeCoarseLocation) { ServiceState state = new ServiceState(this); - if (state.mNetworkRegistrationInfos != null) { - state.mNetworkRegistrationInfos = state.mNetworkRegistrationInfos.stream() - .map(NetworkRegistrationInfo::sanitizeLocationInfo) - .collect(Collectors.toList()); + synchronized (state.mNetworkRegistrationInfos) { + List<NetworkRegistrationInfo> networkRegistrationInfos = + state.mNetworkRegistrationInfos.stream() + .map(NetworkRegistrationInfo::sanitizeLocationInfo) + .collect(Collectors.toList()); + state.mNetworkRegistrationInfos.clear(); + state.mNetworkRegistrationInfos.addAll(networkRegistrationInfos); } if (!removeCoarseLocation) return state; 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/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)) |