Merge "Check FontFamily null before adding it." into sc-dev
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
index afd8e29..ac63653 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
@@ -144,7 +144,7 @@
while (state.keepRunning()) {
state.pauseTiming();
// Invalidate cache.
- resourcesManager.applyConfigurationToResourcesLocked(
+ resourcesManager.applyConfigurationToResources(
resourcesManager.getConfiguration(), null);
state.resumeTiming();
diff --git a/apex/appsearch/Android.bp b/apex/appsearch/Android.bp
index 87f65a9..c592d13 100644
--- a/apex/appsearch/Android.bp
+++ b/apex/appsearch/Android.bp
@@ -24,8 +24,8 @@
apex {
name: "com.android.appsearch",
manifest: "apex_manifest.json",
+ bootclasspath_fragments: ["com.android.appsearch-bootclasspath-fragment"],
java_libs: [
- "framework-appsearch",
"service-appsearch",
],
key: "com.android.appsearch.key",
@@ -45,3 +45,10 @@
// com.android.appsearch.pk8 (the private key)
certificate: "com.android.appsearch",
}
+
+// Encapsulate the contributions made by the com.android.appsearch to the bootclasspath.
+bootclasspath_fragment {
+ name: "com.android.appsearch-bootclasspath-fragment",
+ contents: ["framework-appsearch"],
+ apex_available: ["com.android.appsearch"],
+}
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index b096537..8b824e8 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -152,6 +152,8 @@
* this broadcast will be sent. Applications can reschedule all the necessary alarms when
* receiving it.
*
+ * <p>This broadcast will <em>not</em> be sent when the user revokes the permission.
+ *
* <p><em>Note:</em>
* Applications are still required to check {@link #canScheduleExactAlarms()}
* before using the above APIs after receiving this broadcast,
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 452be30..96cbed7 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -2629,8 +2629,7 @@
}
} catch (NameNotFoundException e) {
throw new IllegalArgumentException(
- "Tried to schedule job for non-existent package: "
- + service.getPackageName());
+ "Tried to schedule job for non-existent component: " + service);
}
}
diff --git a/core/api/current.txt b/core/api/current.txt
index cb61bbc..98c2d40 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -11154,6 +11154,7 @@
field public static final String ACTION_VIEW = "android.intent.action.VIEW";
field public static final String ACTION_VIEW_LOCUS = "android.intent.action.VIEW_LOCUS";
field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE = "android.intent.action.VIEW_PERMISSION_USAGE";
+ field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD = "android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD";
field public static final String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
field @Deprecated public static final String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED";
field public static final String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH";
@@ -11206,6 +11207,7 @@
field public static final String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
field public static final String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
field public static final String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID";
+ field public static final String EXTRA_ATTRIBUTION_TAGS = "android.intent.extra.ATTRIBUTION_TAGS";
field public static final String EXTRA_AUTO_LAUNCH_SINGLE_CHOICE = "android.intent.extra.AUTO_LAUNCH_SINGLE_CHOICE";
field public static final String EXTRA_BCC = "android.intent.extra.BCC";
field public static final String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
@@ -11231,6 +11233,7 @@
field public static final String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
field public static final String EXTRA_DURATION_MILLIS = "android.intent.extra.DURATION_MILLIS";
field public static final String EXTRA_EMAIL = "android.intent.extra.EMAIL";
+ field public static final String EXTRA_END_TIME = "android.intent.extra.END_TIME";
field public static final String EXTRA_EXCLUDE_COMPONENTS = "android.intent.extra.EXCLUDE_COMPONENTS";
field public static final String EXTRA_FROM_STORAGE = "android.intent.extra.FROM_STORAGE";
field public static final String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
@@ -11245,6 +11248,7 @@
field public static final String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
field public static final String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
field public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
+ field public static final String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME";
field public static final String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
field public static final String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
field public static final String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
@@ -11267,6 +11271,7 @@
field @Deprecated public static final String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
field public static final String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY";
field public static final String EXTRA_SPLIT_NAME = "android.intent.extra.SPLIT_NAME";
+ field public static final String EXTRA_START_TIME = "android.intent.extra.START_TIME";
field public static final String EXTRA_STREAM = "android.intent.extra.STREAM";
field public static final String EXTRA_SUBJECT = "android.intent.extra.SUBJECT";
field public static final String EXTRA_SUSPENDED_PACKAGE_EXTRAS = "android.intent.extra.SUSPENDED_PACKAGE_EXTRAS";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 96a23b2..d30d59f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2399,12 +2399,9 @@
field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
- field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD = "android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD";
field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
- field public static final String EXTRA_ATTRIBUTION_TAGS = "android.intent.extra.ATTRIBUTION_TAGS";
field public static final String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE";
- field public static final String EXTRA_END_TIME = "android.intent.extra.END_TIME";
field public static final String EXTRA_FORCE_FACTORY_RESET = "android.intent.extra.FORCE_FACTORY_RESET";
field public static final String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION";
field public static final String EXTRA_INSTANT_APP_BUNDLES = "android.intent.extra.INSTANT_APP_BUNDLES";
@@ -2416,13 +2413,11 @@
field public static final String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
field public static final String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID";
field public static final String EXTRA_PACKAGES = "android.intent.extra.PACKAGES";
- field public static final String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME";
field public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
field public static final String EXTRA_REASON = "android.intent.extra.REASON";
field public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
field public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
- field public static final String EXTRA_START_TIME = "android.intent.extra.START_TIME";
field public static final String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP";
field public static final String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
field public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 67108864; // 0x4000000
@@ -10473,7 +10468,7 @@
method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int setParameter(int, int);
method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int);
method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition();
- method @Nullable public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle);
+ method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle);
method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean stopRecognition();
method public final void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory);
field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
@@ -10498,7 +10493,7 @@
method public abstract void onAvailabilityChanged(int);
method public void onHotwordDetectionServiceInitialized(int);
method public void onHotwordDetectionServiceRestarted();
- method public void onRejected(@Nullable android.service.voice.HotwordRejectedResult);
+ method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult);
}
public static class AlwaysOnHotwordDetector.EventPayload {
@@ -10559,8 +10554,8 @@
}
public static final class HotwordDetectionService.Callback {
- method public void onDetected(@Nullable android.service.voice.HotwordDetectedResult);
- method public void onRejected(@Nullable android.service.voice.HotwordRejectedResult);
+ method public void onDetected(@NonNull android.service.voice.HotwordDetectedResult);
+ method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult);
}
public interface HotwordDetector {
@@ -10581,7 +10576,7 @@
method public void onHotwordDetectionServiceRestarted();
method public void onRecognitionPaused();
method public void onRecognitionResumed();
- method public void onRejected(@Nullable android.service.voice.HotwordRejectedResult);
+ method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult);
}
public final class HotwordRejectedResult implements android.os.Parcelable {
@@ -14919,6 +14914,7 @@
public static interface WebViewProvider.ViewDelegate {
method public default void autofill(android.util.SparseArray<android.view.autofill.AutofillValue>);
+ method public default void dispatchCreateViewTranslationRequest(@NonNull java.util.Map<android.view.autofill.AutofillId,long[]>, @NonNull int[], @Nullable android.view.translation.TranslationCapability, @NonNull java.util.List<android.view.translation.ViewTranslationRequest>);
method public boolean dispatchKeyEvent(android.view.KeyEvent);
method public android.view.View findFocus(android.view.View);
method public android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f458107..c8e365e 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -865,6 +865,10 @@
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ProviderInfoList> CREATOR;
}
+ public final class SharedLibraryInfo implements android.os.Parcelable {
+ method @NonNull public java.util.List<java.lang.String> getAllCodePaths();
+ }
+
public final class ShortcutInfo implements android.os.Parcelable {
method public boolean isVisibleToPublisher();
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ff210e1..a667767 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5887,7 +5887,7 @@
public final void applyConfigurationToResources(Configuration config) {
synchronized (mResourcesManager) {
- mResourcesManager.applyConfigurationToResourcesLocked(config, null);
+ mResourcesManager.applyConfigurationToResources(config, null);
}
}
@@ -5975,7 +5975,7 @@
synchronized (mResourcesManager) {
// Update all affected Resources objects to use new ResourcesImpl
- mResourcesManager.applyNewResourceDirsLocked(ai, oldResDirs);
+ mResourcesManager.applyNewResourceDirs(ai, oldResDirs);
}
}
@@ -6231,7 +6231,7 @@
synchronized (mResourcesManager) {
// Update affected Resources objects to use new ResourcesImpl
- mResourcesManager.applyNewResourceDirsLocked(aInfo, oldResDirs);
+ mResourcesManager.applyNewResourceDirs(aInfo, oldResDirs);
}
} catch (RemoteException e) {
}
@@ -6474,7 +6474,7 @@
* reflect configuration changes. The configuration object passed
* in AppBindData can be safely assumed to be up to date
*/
- mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
+ mResourcesManager.applyConfigurationToResources(data.config, data.compatInfo);
mCurDefaultDisplayDpi = data.config.densityDpi;
// This calls mResourcesManager so keep it within the synchronized block.
@@ -7509,7 +7509,7 @@
// We need to apply this change to the resources immediately, because upon returning
// the view hierarchy will be informed about it.
- if (mResourcesManager.applyConfigurationToResourcesLocked(globalConfig,
+ if (mResourcesManager.applyConfigurationToResources(globalConfig,
null /* compat */,
mInitialApplication.getResources().getDisplayAdjustments())) {
mConfigurationController.updateLocaleListFromAppContext(
diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java
index 0dbe3ba..6d92201 100644
--- a/core/java/android/app/ConfigurationController.java
+++ b/core/java/android/app/ConfigurationController.java
@@ -107,8 +107,7 @@
mCompatConfiguration = new Configuration();
}
mCompatConfiguration.setTo(mConfiguration);
- if (mResourcesManager.applyCompatConfigurationLocked(displayDensity,
- mCompatConfiguration)) {
+ if (mResourcesManager.applyCompatConfiguration(displayDensity, mCompatConfiguration)) {
config = mCompatConfiguration;
}
return config;
@@ -199,7 +198,7 @@
// configuration also needs to set to the adjustments for consistency.
appResources.getDisplayAdjustments().getConfiguration().updateFrom(config);
}
- mResourcesManager.applyConfigurationToResourcesLocked(config, compat,
+ mResourcesManager.applyConfigurationToResources(config, compat,
appResources.getDisplayAdjustments());
updateLocaleListFromAppContext(app.getApplicationContext());
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index c6847aa..c6989ad 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -174,6 +174,7 @@
*/
public static final @ServiceNotificationPolicy int FOREGROUND_SERVICE_DEFERRED = 2;
+ @ServiceNotificationPolicy
private int mFgsDeferBehavior;
/**
@@ -4614,9 +4615,9 @@
* foreground service. By default, the system can choose to defer
* visibility of the notification for a short time after the service is
* started. Pass
- * {@link Notification#FOREGROUND_SERVICE_IMMEDIATE BEHAVIOR_IMMEDIATE_DISPLAY}
+ * {@link Notification#FOREGROUND_SERVICE_IMMEDIATE FOREGROUND_SERVICE_IMMEDIATE}
* to this method in order to guarantee that visibility is never deferred. Pass
- * {@link Notification#FOREGROUND_SERVICE_DEFERRED BEHAVIOR_DEFERRED_DISPLAY}
+ * {@link Notification#FOREGROUND_SERVICE_DEFERRED FOREGROUND_SERVICE_DEFERRED}
* to request that visibility is deferred whenever possible.
*
* <p class="note">Note that deferred visibility is not guaranteed. There
@@ -4624,13 +4625,13 @@
* service's associated Notification immediately even when the app has used
* this method to explicitly request deferred display.</p>
* @param behavior One of
- * {@link Notification#FOREGROUND_SERVICE_DEFAULT BEHAVIOR_DEFAULT},
- * {@link Notification#FOREGROUND_SERVICE_IMMEDIATE BEHAVIOR_IMMEDIATE_DISPLAY},
- * or {@link Notification#FOREGROUND_SERVICE_DEFERRED BEHAVIOR_DEFERRED_DISPLAY}
+ * {@link Notification#FOREGROUND_SERVICE_DEFAULT FOREGROUND_SERVICE_DEFAULT},
+ * {@link Notification#FOREGROUND_SERVICE_IMMEDIATE FOREGROUND_SERVICE_IMMEDIATE},
+ * or {@link Notification#FOREGROUND_SERVICE_DEFERRED FOREGROUND_SERVICE_DEFERRED}
* @return
*/
@NonNull
- public Builder setForegroundServiceBehavior(int behavior) {
+ public Builder setForegroundServiceBehavior(@ServiceNotificationPolicy int behavior) {
mN.mFgsDeferBehavior = behavior;
return this;
}
@@ -5309,8 +5310,7 @@
// the change's state in NotificationManagerService were very complex. These behavior
// changes are entirely visual, and should otherwise be undetectable by apps.
@SuppressWarnings("AndroidFrameworkCompatChange")
- private void calculateLargeIconDimens(boolean largeIconShown,
- @NonNull StandardTemplateParams p,
+ private void calculateRightIconDimens(Icon rightIcon, boolean isPromotedPicture,
@NonNull TemplateBindResult result) {
final Resources resources = mContext.getResources();
final float density = resources.getDisplayMetrics().density;
@@ -5323,9 +5323,9 @@
final float viewHeightDp = resources.getDimension(
R.dimen.notification_right_icon_size) / density;
float viewWidthDp = viewHeightDp; // icons are 1:1 by default
- if (largeIconShown && (p.mPromotePicture
+ if (rightIcon != null && (isPromotedPicture
|| mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S)) {
- Drawable drawable = mN.mLargeIcon.loadDrawable(mContext);
+ Drawable drawable = rightIcon.loadDrawable(mContext);
if (drawable != null) {
int iconWidth = drawable.getIntrinsicWidth();
int iconHeight = drawable.getIntrinsicHeight();
@@ -5337,7 +5337,7 @@
}
}
final float extraMarginEndDpIfVisible = viewWidthDp + iconMarginDp;
- result.setRightIconState(largeIconShown, viewWidthDp,
+ result.setRightIconState(rightIcon != null /* visible */, viewWidthDp,
extraMarginEndDpIfVisible, expanderSizeDp);
}
@@ -5349,19 +5349,43 @@
if (mN.mLargeIcon == null && mN.largeIcon != null) {
mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
}
- boolean showLargeIcon = mN.mLargeIcon != null && !p.hideLargeIcon;
- calculateLargeIconDimens(showLargeIcon, p, result);
- if (showLargeIcon) {
+
+ // Determine the left and right icons
+ Icon leftIcon = p.mHideLeftIcon ? null : mN.mLargeIcon;
+ Icon rightIcon = p.mHideRightIcon ? null
+ : (p.mPromotedPicture != null ? p.mPromotedPicture : mN.mLargeIcon);
+
+ // Apply the left icon (without duplicating the bitmap)
+ if (leftIcon != rightIcon || leftIcon == null) {
+ // If the leftIcon is explicitly hidden or different from the rightIcon, then set it
+ // explicitly and make sure it won't take the right_icon drawable.
+ contentView.setImageViewIcon(R.id.left_icon, leftIcon);
+ contentView.setIntTag(R.id.left_icon, R.id.tag_uses_right_icon_drawable, 0);
+ } else {
+ // If the leftIcon equals the rightIcon, just set the flag to use the right_icon
+ // drawable. This avoids the view having two copies of the same bitmap.
+ contentView.setIntTag(R.id.left_icon, R.id.tag_uses_right_icon_drawable, 1);
+ }
+
+ // Always calculate dimens to populate `result` for the GONE case
+ boolean isPromotedPicture = p.mPromotedPicture != null;
+ calculateRightIconDimens(rightIcon, isPromotedPicture, result);
+
+ // Bind the right icon
+ if (rightIcon != null) {
contentView.setViewLayoutWidth(R.id.right_icon,
result.mRightIconWidthDp, TypedValue.COMPLEX_UNIT_DIP);
contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
- contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
- processLargeLegacyIcon(mN.mLargeIcon, contentView, p);
+ contentView.setImageViewIcon(R.id.right_icon, rightIcon);
+ contentView.setIntTag(R.id.right_icon, R.id.tag_keep_when_showing_left_icon,
+ isPromotedPicture ? 1 : 0);
+ processLargeLegacyIcon(rightIcon, contentView, p);
} else {
// The "reset" doesn't clear the drawable, so we do it here. This clear is
// important because the presence of a drawable in this view (regardless of the
// visibility) is used by NotificationGroupingUtil to set the visibility.
contentView.setImageViewIcon(R.id.right_icon, null);
+ contentView.setIntTag(R.id.right_icon, R.id.tag_keep_when_showing_left_icon, 0);
}
}
@@ -6048,11 +6072,13 @@
.viewType(StandardTemplateParams.VIEW_TYPE_MINIMIZED)
.highlightExpander(false)
.fillTextsFrom(this);
- if (!useRegularSubtext || TextUtils.isEmpty(mParams.summaryText)) {
+ if (!useRegularSubtext || TextUtils.isEmpty(p.summaryText)) {
p.summaryText(createSummaryText());
}
RemoteViews header = makeNotificationHeader(p);
header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
+ // The low priority header has no app name and shows the text
+ header.setBoolean(R.id.notification_header, "styleTextAsTitle", true);
return header;
}
@@ -7397,25 +7423,11 @@
return super.makeContentView(increasedHeight);
}
- Icon oldLargeIcon = mBuilder.mN.mLargeIcon;
- mBuilder.mN.mLargeIcon = mPictureIcon;
- // The legacy largeIcon might not allow us to clear the image, as it's taken in
- // replacement if the other one is null. Because we're restoring these legacy icons
- // for old listeners, this is in general non-null.
- Bitmap largeIconLegacy = mBuilder.mN.largeIcon;
- mBuilder.mN.largeIcon = null;
-
StandardTemplateParams p = mBuilder.mParams.reset()
.viewType(StandardTemplateParams.VIEW_TYPE_NORMAL)
.fillTextsFrom(mBuilder)
- .promotePicture(true);
- RemoteViews contentView = getStandardView(mBuilder.getBaseLayoutResource(),
- p, null /* result */);
-
- mBuilder.mN.mLargeIcon = oldLargeIcon;
- mBuilder.mN.largeIcon = largeIconLegacy;
-
- return contentView;
+ .promotedPicture(mPictureIcon);
+ return getStandardView(mBuilder.getBaseLayoutResource(), p, null /* result */);
}
/**
@@ -7427,25 +7439,11 @@
return super.makeHeadsUpContentView(increasedHeight);
}
- Icon oldLargeIcon = mBuilder.mN.mLargeIcon;
- mBuilder.mN.mLargeIcon = mPictureIcon;
- // The legacy largeIcon might not allow us to clear the image, as it's taken in
- // replacement if the other one is null. Because we're restoring these legacy icons
- // for old listeners, this is in general non-null.
- Bitmap largeIconLegacy = mBuilder.mN.largeIcon;
- mBuilder.mN.largeIcon = null;
-
StandardTemplateParams p = mBuilder.mParams.reset()
.viewType(StandardTemplateParams.VIEW_TYPE_HEADS_UP)
.fillTextsFrom(mBuilder)
- .promotePicture(true);
- RemoteViews contentView = getStandardView(mBuilder.getHeadsUpBaseLayoutResource(),
- p, null /* result */);
-
- mBuilder.mN.mLargeIcon = oldLargeIcon;
- mBuilder.mN.largeIcon = largeIconLegacy;
-
- return contentView;
+ .promotedPicture(mPictureIcon);
+ return getStandardView(mBuilder.getHeadsUpBaseLayoutResource(), p, null /* result */);
}
/**
@@ -7540,14 +7538,21 @@
mShowBigPictureWhenCollapsed = extras.getBoolean(EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED);
+ mPictureIcon = getPictureIcon(extras);
+ }
+
+ /** @hide */
+ @Nullable
+ public static Icon getPictureIcon(@Nullable Bundle extras) {
+ if (extras == null) return null;
// When this style adds a picture, we only add one of the keys. If both were added,
// it would most likely be a legacy app trying to override the picture in some way.
// Because of that case it's better to give precedence to the legacy field.
Bitmap bitmapPicture = extras.getParcelable(EXTRA_PICTURE);
if (bitmapPicture != null) {
- mPictureIcon = Icon.createWithBitmap(bitmapPicture);
+ return Icon.createWithBitmap(bitmapPicture);
} else {
- mPictureIcon = extras.getParcelable(EXTRA_PICTURE_ICON);
+ return extras.getParcelable(EXTRA_PICTURE_ICON);
}
}
@@ -8323,7 +8328,8 @@
.hideProgress(true)
.title(isHeaderless ? conversationTitle : null)
.text(null)
- .hideLargeIcon(hideRightIcons || isOneToOne)
+ .hideLeftIcon(isOneToOne)
+ .hideRightIcon(hideRightIcons || isOneToOne)
.headerTextSecondary(isHeaderless ? null : conversationTitle);
RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
isConversationLayout
@@ -9121,9 +9127,10 @@
StandardTemplateParams p = mBuilder.mParams.reset()
.viewType(StandardTemplateParams.VIEW_TYPE_NORMAL)
- .hideTime(numActionsToShow > 1) // hide if actions wider than a large icon
- .hideSubText(numActionsToShow > 1) // hide if actions wider than a large icon
- .hideLargeIcon(numActionsToShow > 0) // large icon or actions; not both
+ .hideTime(numActionsToShow > 1) // hide if actions wider than a right icon
+ .hideSubText(numActionsToShow > 1) // hide if actions wider than a right icon
+ .hideLeftIcon(false) // allow large icon on left when grouped
+ .hideRightIcon(numActionsToShow > 0) // right icon or actions; not both
.hideProgress(true)
.fillTextsFrom(mBuilder);
TemplateBindResult result = new TemplateBindResult();
@@ -9527,7 +9534,8 @@
.viewType(viewType)
.callStyleActions(true)
.allowTextWithProgress(true)
- .hideLargeIcon(true)
+ .hideLeftIcon(true)
+ .hideRightIcon(true)
.hideAppName(isCollapsed)
.titleViewId(R.id.conversation_text)
.title(title)
@@ -12237,7 +12245,9 @@
boolean mHideActions;
boolean mHideProgress;
boolean mHideSnoozeButton;
- boolean mPromotePicture;
+ boolean mHideLeftIcon;
+ boolean mHideRightIcon;
+ Icon mPromotedPicture;
boolean mCallStyleActions;
boolean mAllowTextWithProgress;
int mTitleViewId;
@@ -12247,7 +12257,6 @@
CharSequence headerTextSecondary;
CharSequence summaryText;
int maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
- boolean hideLargeIcon;
boolean allowColorization = true;
boolean mHighlightExpander = false;
@@ -12261,7 +12270,9 @@
mHideActions = false;
mHideProgress = false;
mHideSnoozeButton = false;
- mPromotePicture = false;
+ mHideLeftIcon = false;
+ mHideRightIcon = false;
+ mPromotedPicture = null;
mCallStyleActions = false;
mAllowTextWithProgress = false;
mTitleViewId = R.id.title;
@@ -12271,7 +12282,6 @@
summaryText = null;
headerTextSecondary = null;
maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
- hideLargeIcon = false;
allowColorization = true;
mHighlightExpander = false;
return this;
@@ -12336,8 +12346,8 @@
return this;
}
- final StandardTemplateParams promotePicture(boolean promotePicture) {
- this.mPromotePicture = promotePicture;
+ final StandardTemplateParams promotedPicture(Icon promotedPicture) {
+ this.mPromotedPicture = promotedPicture;
return this;
}
@@ -12371,8 +12381,14 @@
return this;
}
- final StandardTemplateParams hideLargeIcon(boolean hideLargeIcon) {
- this.hideLargeIcon = hideLargeIcon;
+
+ final StandardTemplateParams hideLeftIcon(boolean hideLeftIcon) {
+ this.mHideLeftIcon = hideLeftIcon;
+ return this;
+ }
+
+ final StandardTemplateParams hideRightIcon(boolean hideRightIcon) {
+ this.mHideRightIcon = hideRightIcon;
return this;
}
@@ -12409,7 +12425,8 @@
// Minimally decorated custom views do not show certain pieces of chrome that have
// always been shown when using DecoratedCustomViewStyle.
boolean hideOtherFields = decorationType <= DECORATION_MINIMAL;
- hideLargeIcon(hideOtherFields);
+ hideLeftIcon(false); // The left icon decoration is better than showing nothing.
+ hideRightIcon(hideOtherFields);
hideProgress(hideOtherFields);
hideActions(hideOtherFields);
return this;
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 74134e1..792336d 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -75,6 +75,11 @@
private static ResourcesManager sResourcesManager;
/**
+ * Internal lock object
+ */
+ private final Object mLock = new Object();
+
+ /**
* The global compatibility settings.
*/
private CompatibilityInfo mResCompatibilityInfo;
@@ -275,7 +280,7 @@
* try as hard as possible to release any open FDs.
*/
public void invalidatePath(String path) {
- synchronized (this) {
+ synchronized (mLock) {
int count = 0;
for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
@@ -304,7 +309,7 @@
}
public Configuration getConfiguration() {
- synchronized (this) {
+ synchronized (mLock) {
return mResConfiguration;
}
}
@@ -351,13 +356,15 @@
config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp;
}
- public boolean applyCompatConfigurationLocked(int displayDensity,
+ public boolean applyCompatConfiguration(int displayDensity,
@NonNull Configuration compatConfiguration) {
- if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) {
- mResCompatibilityInfo.applyToConfiguration(displayDensity, compatConfiguration);
- return true;
+ synchronized (mLock) {
+ if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) {
+ mResCompatibilityInfo.applyToConfiguration(displayDensity, compatConfiguration);
+ return true;
+ }
+ return false;
}
- return false;
}
/**
@@ -376,7 +383,7 @@
final Pair<Integer, DisplayAdjustments> key =
Pair.create(displayId, displayAdjustmentsCopy);
SoftReference<Display> sd;
- synchronized (this) {
+ synchronized (mLock) {
sd = mAdjustedDisplays.get(key);
}
if (sd != null) {
@@ -392,7 +399,7 @@
}
final Display display = dm.getCompatibleDisplay(displayId, key.second);
if (display != null) {
- synchronized (this) {
+ synchronized (mLock) {
mAdjustedDisplays.put(key, new SoftReference<>(display));
}
}
@@ -407,7 +414,7 @@
* @param resources The {@link Resources} backing the display adjustments.
*/
public Display getAdjustedDisplay(final int displayId, Resources resources) {
- synchronized (this) {
+ synchronized (mLock) {
final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
if (dm == null) {
// may be null early in system startup
@@ -425,7 +432,7 @@
ApkAssets apkAssets;
// Optimistically check if this ApkAssets exists somewhere else.
- synchronized (this) {
+ synchronized (mLock) {
final WeakReference<ApkAssets> apkAssetsRef = mCachedApkAssets.get(key);
if (apkAssetsRef != null) {
apkAssets = apkAssetsRef.get();
@@ -447,7 +454,7 @@
key.sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
}
- synchronized (this) {
+ synchronized (mLock) {
mCachedApkAssets.put(key, new WeakReference<>(apkAssets));
}
@@ -559,7 +566,7 @@
* @hide
*/
public void dump(String prefix, PrintWriter printWriter) {
- synchronized (this) {
+ synchronized (mLock) {
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
for (int i = 0; i < prefix.length() / 2; i++) {
pw.increaseIndent();
@@ -688,7 +695,7 @@
*/
boolean isSameResourcesOverrideConfig(@Nullable IBinder activityToken,
@Nullable Configuration overrideConfig) {
- synchronized (this) {
+ synchronized (mLock) {
final ActivityResources activityResources
= activityToken != null ? mActivityResourceReferences.get(activityToken) : null;
if (activityResources == null) {
@@ -834,7 +841,7 @@
+ " with key=" + key);
}
- synchronized (this) {
+ synchronized (mLock) {
// Force the creation of an ActivityResourcesStruct.
getOrCreateActivityResourcesStructLocked(token);
}
@@ -842,7 +849,7 @@
// Update any existing Activity Resources references.
updateResourcesForActivity(token, overrideConfig, displayId);
- synchronized (this) {
+ synchronized (mLock) {
Resources resources = findResourcesForActivityLocked(token, key,
classLoader);
if (resources != null) {
@@ -868,7 +875,7 @@
*/
private void rebaseKeyForActivity(IBinder activityToken, ResourcesKey key,
boolean overridesActivityDisplay) {
- synchronized (this) {
+ synchronized (mLock) {
final ActivityResources activityResources =
getOrCreateActivityResourcesStructLocked(activityToken);
@@ -960,7 +967,7 @@
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#createApkAssetsSupplierNotLocked");
try {
- if (DEBUG && Thread.holdsLock(this)) {
+ if (DEBUG && Thread.holdsLock(mLock)) {
Slog.w(TAG, "Calling thread " + Thread.currentThread().getName()
+ " is holding mLock", new Throwable());
}
@@ -994,7 +1001,7 @@
@Nullable
private Resources createResources(@NonNull ResourcesKey key, @NonNull ClassLoader classLoader,
@Nullable ApkAssetsSupplier apkSupplier) {
- synchronized (this) {
+ synchronized (mLock) {
if (DEBUG) {
Throwable here = new Throwable();
here.fillInStackTrace();
@@ -1015,7 +1022,7 @@
@NonNull ResourcesKey key, @NonNull Configuration initialOverrideConfig,
@Nullable Integer overrideDisplayId, @NonNull ClassLoader classLoader,
@Nullable ApkAssetsSupplier apkSupplier) {
- synchronized (this) {
+ synchronized (mLock) {
if (DEBUG) {
Throwable here = new Throwable();
here.fillInStackTrace();
@@ -1130,7 +1137,7 @@
if (displayId == INVALID_DISPLAY) {
throw new IllegalArgumentException("displayId can not be INVALID_DISPLAY");
}
- synchronized (this) {
+ synchronized (mLock) {
final ActivityResources activityResources =
getOrCreateActivityResourcesStructLocked(activityToken);
@@ -1269,67 +1276,64 @@
public final boolean applyConfigurationToResources(@NonNull Configuration config,
@Nullable CompatibilityInfo compat) {
- synchronized(this) {
- return applyConfigurationToResourcesLocked(config, compat, null /* adjustments */);
- }
- }
-
- public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
- @Nullable CompatibilityInfo compat) {
- return applyConfigurationToResourcesLocked(config, compat, null /* adjustments */);
+ return applyConfigurationToResources(config, compat, null /* adjustments */);
}
/** Applies the global configuration to the managed resources. */
- public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
+ public final boolean applyConfigurationToResources(@NonNull Configuration config,
@Nullable CompatibilityInfo compat, @Nullable DisplayAdjustments adjustments) {
- try {
- Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
- "ResourcesManager#applyConfigurationToResourcesLocked");
+ synchronized (mLock) {
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+ "ResourcesManager#applyConfigurationToResources");
- if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
- if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
- + mResConfiguration.seq + ", newSeq=" + config.seq);
- return false;
- }
-
- // Things might have changed in display manager, so clear the cached displays.
- mAdjustedDisplays.clear();
-
- int changes = mResConfiguration.updateFrom(config);
- if (compat != null && (mResCompatibilityInfo == null ||
- !mResCompatibilityInfo.equals(compat))) {
- mResCompatibilityInfo = compat;
- changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
- | ActivityInfo.CONFIG_SCREEN_SIZE
- | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
- }
-
- DisplayMetrics displayMetrics = getDisplayMetrics();
- if (adjustments != null) {
- // Currently the only case where the adjustment takes effect is to simulate placing
- // an app in a rotated display.
- adjustments.adjustGlobalAppMetrics(displayMetrics);
- }
- Resources.updateSystemConfiguration(config, displayMetrics, compat);
-
- ApplicationPackageManager.configurationChanged();
-
- Configuration tmpConfig = new Configuration();
-
- for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
- ResourcesKey key = mResourceImpls.keyAt(i);
- WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
- ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
- if (r != null) {
- applyConfigurationToResourcesLocked(config, compat, tmpConfig, key, r);
- } else {
- mResourceImpls.removeAt(i);
+ if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
+ if (DEBUG || DEBUG_CONFIGURATION) {
+ Slog.v(TAG, "Skipping new config: curSeq="
+ + mResConfiguration.seq + ", newSeq=" + config.seq);
+ }
+ return false;
}
- }
- return changes != 0;
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ // Things might have changed in display manager, so clear the cached displays.
+ mAdjustedDisplays.clear();
+
+ int changes = mResConfiguration.updateFrom(config);
+ if (compat != null && (mResCompatibilityInfo == null
+ || !mResCompatibilityInfo.equals(compat))) {
+ mResCompatibilityInfo = compat;
+ changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
+ | ActivityInfo.CONFIG_SCREEN_SIZE
+ | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+ }
+
+ DisplayMetrics displayMetrics = getDisplayMetrics();
+ if (adjustments != null) {
+ // Currently the only case where the adjustment takes effect is to simulate
+ // placing an app in a rotated display.
+ adjustments.adjustGlobalAppMetrics(displayMetrics);
+ }
+ Resources.updateSystemConfiguration(config, displayMetrics, compat);
+
+ ApplicationPackageManager.configurationChanged();
+
+ Configuration tmpConfig = new Configuration();
+
+ for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
+ ResourcesKey key = mResourceImpls.keyAt(i);
+ WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+ ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
+ if (r != null) {
+ applyConfigurationToResourcesLocked(config, compat, tmpConfig, key, r);
+ } else {
+ mResourceImpls.removeAt(i);
+ }
+ }
+
+ return changes != 0;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ }
}
}
@@ -1378,7 +1382,7 @@
* @param libAssets The library asset paths to add.
*/
public void appendLibAssetsForMainAssetPath(String assetPath, String[] libAssets) {
- synchronized (this) {
+ synchronized (mLock) {
// Record which ResourcesImpl need updating
// (and what ResourcesKey they should update to).
final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
@@ -1414,54 +1418,56 @@
}
// TODO(adamlesinski): Make this accept more than just overlay directories.
- final void applyNewResourceDirsLocked(@NonNull final ApplicationInfo appInfo,
+ void applyNewResourceDirs(@NonNull final ApplicationInfo appInfo,
@Nullable final String[] oldPaths) {
- try {
- Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
- "ResourcesManager#applyNewResourceDirsLocked");
+ synchronized (mLock) {
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+ "ResourcesManager#applyNewResourceDirsLocked");
- String baseCodePath = appInfo.getBaseCodePath();
+ String baseCodePath = appInfo.getBaseCodePath();
- final int myUid = Process.myUid();
- String[] newSplitDirs = appInfo.uid == myUid
- ? appInfo.splitSourceDirs
- : appInfo.splitPublicSourceDirs;
+ final int myUid = Process.myUid();
+ String[] newSplitDirs = appInfo.uid == myUid
+ ? appInfo.splitSourceDirs
+ : appInfo.splitPublicSourceDirs;
- // ApplicationInfo is mutable, so clone the arrays to prevent outside modification
- String[] copiedSplitDirs = ArrayUtils.cloneOrNull(newSplitDirs);
- String[] copiedResourceDirs = combinedOverlayPaths(appInfo.resourceDirs,
- appInfo.overlayPaths);
+ // ApplicationInfo is mutable, so clone the arrays to prevent outside modification
+ String[] copiedSplitDirs = ArrayUtils.cloneOrNull(newSplitDirs);
+ String[] copiedResourceDirs = combinedOverlayPaths(appInfo.resourceDirs,
+ appInfo.overlayPaths);
- final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
- final int implCount = mResourceImpls.size();
- for (int i = 0; i < implCount; i++) {
- final ResourcesKey key = mResourceImpls.keyAt(i);
- final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
- final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
+ final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
+ final int implCount = mResourceImpls.size();
+ for (int i = 0; i < implCount; i++) {
+ final ResourcesKey key = mResourceImpls.keyAt(i);
+ final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+ final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
- if (impl == null) {
- continue;
+ if (impl == null) {
+ continue;
+ }
+
+ if (key.mResDir == null
+ || key.mResDir.equals(baseCodePath)
+ || ArrayUtils.contains(oldPaths, key.mResDir)) {
+ updatedResourceKeys.put(impl, new ResourcesKey(
+ baseCodePath,
+ copiedSplitDirs,
+ copiedResourceDirs,
+ key.mLibDirs,
+ key.mDisplayId,
+ key.mOverrideConfiguration,
+ key.mCompatInfo,
+ key.mLoaders
+ ));
+ }
}
- if (key.mResDir == null
- || key.mResDir.equals(baseCodePath)
- || ArrayUtils.contains(oldPaths, key.mResDir)) {
- updatedResourceKeys.put(impl, new ResourcesKey(
- baseCodePath,
- copiedSplitDirs,
- copiedResourceDirs,
- key.mLibDirs,
- key.mDisplayId,
- key.mOverrideConfiguration,
- key.mCompatInfo,
- key.mLoaders
- ));
- }
+ redirectResourcesToNewImplLocked(updatedResourceKeys);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
-
- redirectResourcesToNewImplLocked(updatedResourceKeys);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
@@ -1556,7 +1562,7 @@
public boolean overrideTokenDisplayAdjustments(IBinder token,
@Nullable Consumer<DisplayAdjustments> override) {
boolean handled = false;
- synchronized (this) {
+ synchronized (mLock) {
final ActivityResources tokenResources = mActivityResourceReferences.get(token);
if (tokenResources == null) {
return false;
@@ -1589,7 +1595,7 @@
@Override
public void onLoadersChanged(@NonNull Resources resources,
@NonNull List<ResourcesLoader> newLoader) {
- synchronized (ResourcesManager.this) {
+ synchronized (mLock) {
final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
if (oldKey == null) {
throw new IllegalArgumentException("Cannot modify resource loaders of"
@@ -1617,7 +1623,7 @@
**/
@Override
public void onLoaderUpdated(@NonNull ResourcesLoader loader) {
- synchronized (ResourcesManager.this) {
+ synchronized (mLock) {
final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceImplKeys =
new ArrayMap<>();
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7c7cfdb..bbb49fb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2022,12 +2022,9 @@
* <p>
* Output: Nothing.
* </p>
- *
- * @hide
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE)
- @SystemApi
public static final String ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD =
"android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD";
@@ -2188,12 +2185,7 @@
* <p>
* Type: String
* </p>
- *
- * E.g. {@link android.Manifest.permission_group.CONTACTS}
- *
- * @hide
*/
- @SystemApi
public static final String EXTRA_PERMISSION_GROUP_NAME =
"android.intent.extra.PERMISSION_GROUP_NAME";
@@ -5342,28 +5334,19 @@
* {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
*
* E.g. an attribution tag could be location_provider, com.google.android.gms.*, etc.
- *
- * @hide
*/
- @SystemApi
public static final String EXTRA_ATTRIBUTION_TAGS = "android.intent.extra.ATTRIBUTION_TAGS";
/**
* A long representing the start timestamp (epoch time in millis) of the permission usage
* when used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
- *
- * @hide
*/
- @SystemApi
public static final String EXTRA_START_TIME = "android.intent.extra.START_TIME";
/**
* A long representing the end timestamp (epoch time in millis) of the permission usage when
* used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
- *
- * @hide
*/
- @SystemApi
public static final String EXTRA_END_TIME = "android.intent.extra.END_TIME";
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a1d419e..edf0e57 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4775,8 +4775,7 @@
* @param flags Additional option flags to modify the data returned.
* @return A {@link ServiceInfo} object containing information about the
* service.
- * @throws NameNotFoundException if a package with the given name cannot be
- * found on the system.
+ * @throws NameNotFoundException if the component cannot be found on the system.
*/
@NonNull
public abstract ServiceInfo getServiceInfo(@NonNull ComponentName component,
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index a60e642..13ff602 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -20,6 +20,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -29,6 +30,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* This class provides information for a shared library. There are
@@ -190,7 +192,8 @@
*
* @hide
*/
- public List<String> getAllCodePaths() {
+ @TestApi
+ public @NonNull List<String> getAllCodePaths() {
if (getPath() != null) {
// Builtin library.
ArrayList<String> list = new ArrayList<>();
@@ -198,7 +201,7 @@
return list;
} else {
// Static or dynamic library.
- return mCodePaths;
+ return Objects.requireNonNull(mCodePaths);
}
}
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 8bc3734..1eb4504 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -49,6 +49,9 @@
"include-filter": "android.appsecurity.cts.AppSecurityTests#testPermissionDiffCert"
}
]
+ },
+ {
+ "name": "CtsPackageManagerBootTestCases"
}
]
}
diff --git a/core/java/android/hardware/hdmi/OWNERS b/core/java/android/hardware/hdmi/OWNERS
index 16c15e3..60d43fd 100644
--- a/core/java/android/hardware/hdmi/OWNERS
+++ b/core/java/android/hardware/hdmi/OWNERS
@@ -4,3 +4,4 @@
marvinramin@google.com
nchalko@google.com
+lcnathalie@google.com
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index d41c0b4..caab152 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -52,12 +52,17 @@
private static final String GATEWAY_CONNECTION_CONFIGS_KEY = "mGatewayConnectionConfigs";
@NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs;
+ private static final String IS_TEST_MODE_PROFILE_KEY = "mIsTestModeProfile";
+ private final boolean mIsTestModeProfile;
+
private VcnConfig(
@NonNull String packageName,
- @NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs) {
+ @NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs,
+ boolean isTestModeProfile) {
mPackageName = packageName;
mGatewayConnectionConfigs =
Collections.unmodifiableSet(new ArraySet<>(gatewayConnectionConfigs));
+ mIsTestModeProfile = isTestModeProfile;
validate();
}
@@ -77,6 +82,7 @@
new ArraySet<>(
PersistableBundleUtils.toList(
gatewayConnectionConfigsBundle, VcnGatewayConnectionConfig::new));
+ mIsTestModeProfile = in.getBoolean(IS_TEST_MODE_PROFILE_KEY);
validate();
}
@@ -104,6 +110,15 @@
}
/**
+ * Returns whether or not this VcnConfig is restricted to test networks.
+ *
+ * @hide
+ */
+ public boolean isTestModeProfile() {
+ return mIsTestModeProfile;
+ }
+
+ /**
* Serializes this object to a PersistableBundle.
*
* @hide
@@ -119,13 +134,14 @@
new ArrayList<>(mGatewayConnectionConfigs),
VcnGatewayConnectionConfig::toPersistableBundle);
result.putPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY, gatewayConnectionConfigsBundle);
+ result.putBoolean(IS_TEST_MODE_PROFILE_KEY, mIsTestModeProfile);
return result;
}
@Override
public int hashCode() {
- return Objects.hash(mPackageName, mGatewayConnectionConfigs);
+ return Objects.hash(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile);
}
@Override
@@ -136,7 +152,8 @@
final VcnConfig rhs = (VcnConfig) other;
return mPackageName.equals(rhs.mPackageName)
- && mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs);
+ && mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs)
+ && mIsTestModeProfile == rhs.mIsTestModeProfile;
}
// Parcelable methods
@@ -172,6 +189,8 @@
@NonNull
private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs = new ArraySet<>();
+ private boolean mIsTestModeProfile = false;
+
public Builder(@NonNull Context context) {
Objects.requireNonNull(context, "context was null");
@@ -207,13 +226,29 @@
}
/**
+ * Restricts this VcnConfig to matching with test networks (only).
+ *
+ * <p>This method is for testing only, and must not be used by apps. Calling {@link
+ * VcnManager#setVcnConfig(ParcelUuid, VcnConfig)} with a VcnConfig where test-network usage
+ * is enabled will require the MANAGE_TEST_NETWORKS permission.
+ *
+ * @return this {@link Builder} instance, for chaining
+ * @hide
+ */
+ @NonNull
+ public Builder setIsTestModeProfile() {
+ mIsTestModeProfile = true;
+ return this;
+ }
+
+ /**
* Builds and validates the VcnConfig.
*
* @return an immutable VcnConfig instance
*/
@NonNull
public VcnConfig build() {
- return new VcnConfig(mPackageName, mGatewayConnectionConfigs);
+ return new VcnConfig(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile);
}
}
}
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index f02346b..7eea0b1 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -15,6 +15,8 @@
*/
package android.net.vcn;
+import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
+
import static com.android.internal.annotations.VisibleForTesting.Visibility;
import android.annotation.IntDef;
@@ -438,6 +440,8 @@
* distinguish between VcnGatewayConnectionConfigs configured on a single {@link
* VcnConfig}. This will be used as the identifier in VcnStatusCallback invocations.
* @param tunnelConnectionParams the IKE tunnel connection configuration
+ * @throws IllegalArgumentException if the provided IkeTunnelConnectionParams is not
+ * configured to support MOBIKE
* @see IkeTunnelConnectionParams
* @see VcnManager.VcnStatusCallback#onGatewayConnectionError
*/
@@ -446,6 +450,10 @@
@NonNull IkeTunnelConnectionParams tunnelConnectionParams) {
Objects.requireNonNull(gatewayConnectionName, "gatewayConnectionName was null");
Objects.requireNonNull(tunnelConnectionParams, "tunnelConnectionParams was null");
+ if (!tunnelConnectionParams.getIkeSessionParams().hasIkeOption(IKE_OPTION_MOBIKE)) {
+ throw new IllegalArgumentException(
+ "MOBIKE must be configured for the provided IkeSessionParams");
+ }
mGatewayConnectionName = gatewayConnectionName;
mTunnelConnectionParams = tunnelConnectionParams;
diff --git a/core/java/android/net/vcn/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java
index 0e9ccf1..1f18184 100644
--- a/core/java/android/net/vcn/VcnTransportInfo.java
+++ b/core/java/android/net/vcn/VcnTransportInfo.java
@@ -16,23 +16,17 @@
package android.net.vcn;
-import static android.net.NetworkCapabilities.REDACT_ALL;
-import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
+import static android.net.NetworkCapabilities.REDACT_NONE;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.net.NetworkCapabilities;
import android.net.TransportInfo;
import android.net.wifi.WifiInfo;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.SubscriptionManager;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.util.Objects;
/**
@@ -55,32 +49,17 @@
@Nullable private final WifiInfo mWifiInfo;
private final int mSubId;
- /**
- * The redaction scheme to use when parcelling.
- *
- * <p>The TransportInfo/NetworkCapabilities redaction mechanisms rely on redaction being
- * performed at parcelling time. This means that the redaction scheme must be stored for later
- * use.
- *
- * <p>Since the redaction scheme itself is not parcelled, this field is listed as a transient.
- *
- * <p>Defaults to REDACT_ALL when constructed using public constructors, or creating from
- * parcels.
- */
- private final transient long mRedactions;
-
public VcnTransportInfo(@NonNull WifiInfo wifiInfo) {
- this(wifiInfo, INVALID_SUBSCRIPTION_ID, REDACT_ALL);
+ this(wifiInfo, INVALID_SUBSCRIPTION_ID);
}
public VcnTransportInfo(int subId) {
- this(null /* wifiInfo */, subId, REDACT_ALL);
+ this(null /* wifiInfo */, subId);
}
- private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId, long redactions) {
+ private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId) {
mWifiInfo = wifiInfo;
mSubId = subId;
- mRedactions = redactions;
}
/**
@@ -102,25 +81,14 @@
* SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
*
* @return the Subscription ID if a cellular underlying Network is present, else {@link
- * android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID}.
+ * android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
*/
public int getSubId() {
return mSubId;
}
- /**
- * Gets the redaction scheme
- *
- * @hide
- */
- @VisibleForTesting(visibility = PRIVATE)
- public long getRedaction() {
- return mRedactions;
- }
-
@Override
public int hashCode() {
- // mRedactions not hashed, as it is a transient, for control of parcelling
return Objects.hash(mWifiInfo, mSubId);
}
@@ -128,8 +96,6 @@
public boolean equals(Object o) {
if (!(o instanceof VcnTransportInfo)) return false;
final VcnTransportInfo that = (VcnTransportInfo) o;
-
- // mRedactions not compared, as it is a transient, for control of parcelling
return Objects.equals(mWifiInfo, that.mWifiInfo) && mSubId == that.mSubId;
}
@@ -143,31 +109,19 @@
@NonNull
public TransportInfo makeCopy(long redactions) {
return new VcnTransportInfo(
- mWifiInfo == null ? null : mWifiInfo.makeCopy(redactions), mSubId, redactions);
+ (mWifiInfo == null) ? null : mWifiInfo.makeCopy(redactions), mSubId);
}
@Override
public long getApplicableRedactions() {
- long redactions = REDACT_FOR_NETWORK_SETTINGS;
-
- // Add additional wifi redactions if necessary
- if (mWifiInfo != null) {
- redactions |= mWifiInfo.getApplicableRedactions();
- }
-
- return redactions;
- }
-
- private boolean shouldParcelNetworkSettingsFields() {
- return (mRedactions & NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS) == 0;
+ return (mWifiInfo == null) ? REDACT_NONE : mWifiInfo.getApplicableRedactions();
}
/** {@inheritDoc} */
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(shouldParcelNetworkSettingsFields() ? mSubId : INVALID_SUBSCRIPTION_ID);
- dest.writeParcelable(
- shouldParcelNetworkSettingsFields() ? (Parcelable) mWifiInfo : null, flags);
+ dest.writeInt(mSubId);
+ dest.writeParcelable(mWifiInfo, flags);
}
@Override
@@ -181,17 +135,7 @@
public VcnTransportInfo createFromParcel(Parcel in) {
final int subId = in.readInt();
final WifiInfo wifiInfo = in.readParcelable(null);
-
- // If all fields are their null values, return null TransportInfo to avoid
- // leaking information about this being a VCN Network (instead of macro
- // cellular, etc)
- if (wifiInfo == null && subId == INVALID_SUBSCRIPTION_ID) {
- return null;
- }
-
- // Prevent further forwarding by redacting everything in future parcels from
- // this VcnTransportInfo
- return new VcnTransportInfo(wifiInfo, subId, REDACT_ALL);
+ return new VcnTransportInfo(wifiInfo, subId);
}
public VcnTransportInfo[] newArray(int size) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b8ad068..326012d 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2026,6 +2026,9 @@
case USER_TYPE_SYSTEM_HEADLESS:
return FrameworkStatsLog
.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__SYSTEM_HEADLESS;
+ case USER_TYPE_PROFILE_CLONE:
+ return FrameworkStatsLog
+ .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_CLONE;
default:
return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN;
}
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index d7d1902..1092adf 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -580,6 +580,13 @@
*/
public static final String NAMESPACE_GAME_OVERLAY = "game_overlay";
+ /**
+ * Namespace for Constrain Display APIs related features.
+ *
+ * @hide
+ */
+ public static final String NAMESPACE_CONSTRAIN_DISPLAY_APIS = "constrain_display_apis";
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b879082..e410e50 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8636,7 +8636,6 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
- @Readable
public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
/**
diff --git a/core/java/android/service/voice/AbstractHotwordDetector.java b/core/java/android/service/voice/AbstractHotwordDetector.java
index 4896748..54ccf30 100644
--- a/core/java/android/service/voice/AbstractHotwordDetector.java
+++ b/core/java/android/service/voice/AbstractHotwordDetector.java
@@ -55,10 +55,8 @@
/**
* Detect hotword from an externally supplied stream of data.
*
- * @return a writeable file descriptor that clients can start writing data in the given format.
- * In order to stop detection, clients can close the given stream.
+ * @return true if the request to start recognition succeeded
*/
- @Nullable
@Override
public boolean startRecognition(
@NonNull ParcelFileDescriptor audioStream,
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index bacc6ec..e813017 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -524,7 +524,7 @@
* @param result Info about the second stage detection result, provided by the
* {@link HotwordDetectionService}.
*/
- public void onRejected(@Nullable HotwordRejectedResult result) {
+ public void onRejected(@NonNull HotwordRejectedResult result) {
}
/**
@@ -1164,7 +1164,7 @@
}
@Override
- public void onRejected(HotwordRejectedResult result) {
+ public void onRejected(@NonNull HotwordRejectedResult result) {
if (DBG) {
Slog.d(TAG, "onRejected(" + result + ")");
} else {
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index 473e7ae..ea01e09 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -16,6 +16,8 @@
package android.service.voice;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.DurationMillisLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -414,11 +416,15 @@
}
/**
- * Called when the detected result is valid.
+ * Informs the {@link HotwordDetector} that the keyphrase was detected.
+ *
+ * @param result Info about the detection result. This is provided to the
+ * {@link HotwordDetector}.
*/
- public void onDetected(@Nullable HotwordDetectedResult hotwordDetectedResult) {
+ public void onDetected(@NonNull HotwordDetectedResult result) {
+ requireNonNull(result);
try {
- mRemoteCallback.onDetected(hotwordDetectedResult);
+ mRemoteCallback.onDetected(result);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -433,7 +439,8 @@
* @param result Info about the second stage detection result. This is provided to
* the {@link HotwordDetector}.
*/
- public void onRejected(@Nullable HotwordRejectedResult result) {
+ public void onRejected(@NonNull HotwordRejectedResult result) {
+ requireNonNull(result);
try {
mRemoteCallback.onRejected(result);
} catch (RemoteException e) {
diff --git a/core/java/android/service/voice/HotwordDetector.java b/core/java/android/service/voice/HotwordDetector.java
index 2fb4dbc..d3c10ea 100644
--- a/core/java/android/service/voice/HotwordDetector.java
+++ b/core/java/android/service/voice/HotwordDetector.java
@@ -160,7 +160,7 @@
* @param result Info about the second stage detection result, provided by the
* {@link HotwordDetectionService}.
*/
- void onRejected(@Nullable HotwordRejectedResult result);
+ void onRejected(@NonNull HotwordRejectedResult result);
/**
* Called when the {@link HotwordDetectionService} is created by the system and given a
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 6c8753b..000c685 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -29,6 +29,7 @@
import android.util.AttributeSet;
import android.widget.RelativeLayout;
import android.widget.RemoteViews;
+import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.widget.CachingIconView;
@@ -175,6 +176,28 @@
}
/**
+ * This is used to make the low-priority header show the bolded text of a title.
+ *
+ * @param styleTextAsTitle true if this header's text is to have the style of a title
+ */
+ @RemotableViewMethod
+ public void styleTextAsTitle(boolean styleTextAsTitle) {
+ int styleResId = styleTextAsTitle
+ ? R.style.TextAppearance_DeviceDefault_Notification_Title
+ : R.style.TextAppearance_DeviceDefault_Notification_Info;
+ // Most of the time, we're showing text in the minimized state
+ View headerText = findViewById(R.id.header_text);
+ if (headerText instanceof TextView) {
+ ((TextView) headerText).setTextAppearance(styleResId);
+ }
+ // If there's no summary or text, we show the app name instead of nothing
+ View appNameText = findViewById(R.id.app_name_text);
+ if (appNameText instanceof TextView) {
+ ((TextView) appNameText).setTextAppearance(styleResId);
+ }
+ }
+
+ /**
* Get the current margin end value for the header text.
* Add this to {@link #getTopLineBaseMarginEnd()} to get the total margin of the top line.
*
diff --git a/core/java/android/view/translation/UiTranslationSpec.java b/core/java/android/view/translation/UiTranslationSpec.java
index b43dbce..3d1ebfd 100644
--- a/core/java/android/view/translation/UiTranslationSpec.java
+++ b/core/java/android/view/translation/UiTranslationSpec.java
@@ -20,6 +20,7 @@
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.widget.TextView;
import com.android.internal.util.DataClass;
@@ -35,36 +36,38 @@
public final class UiTranslationSpec implements Parcelable {
/**
- * Whether the original content of the view should be directly modified to include padding that
- * makes it the same size as the translated content. Defaults to {@code false}.
+ * Whether the original content of the view should be made to appear as if it is the same size
+ * as the translated content. Defaults to {@code false}.
* <p>
- * For {@link android.widget.TextView}, the system does not directly modify the original text,
- * rather changes the displayed content using a
- * {@link android.text.method.TransformationMethod}.
- * This can cause issues in apps that do not account for TransformationMethods. For example, an
- * app using DynamicLayout may use the calculated text offsets to operate on the original text,
- * but this can be problematic when the layout was calculated on translated text with a
- * different length.
+ * For {@link TextView}, the system does not directly modify the original text, rather
+ * changes the displayed content using a {@link android.text.method.TransformationMethod}. This
+ * can cause issues in apps that do not account for length-changing TransformationMethods. For
+ * example, an app using DynamicLayout may use the calculated line-offsets to operate on the
+ * original text, but this can cause crashes when the layout was calculated on translated text
+ * with a different length.
* <p>
- * If this is {@code true}, for a TextView the default implementation will append spaces to the
- * text to make the length the same as the translated text.
+ * If this is {@code true}, for a TextView the default implementation appends spaces to the
+ * result of {@link TextView#getText()} to make the length the same as the translated text.
+ * <p>
+ * This only affects apps with target SDK R or lower.
*/
private boolean mShouldPadContentForCompat = false;
/**
- * Whether the original content of the view should be directly modified to include padding that
- * makes it the same size as the translated content.
+ * Whether the original content of the view should be made to appear as if it is the same size
+ * as the translated content.
* <p>
- * For {@link android.widget.TextView}, the system does not directly modify the original text,
- * rather changes the displayed content using a
- * {@link android.text.method.TransformationMethod}.
- * This can cause issues in apps that do not account for TransformationMethods. For example, an
- * app using DynamicLayout may use the calculated text offsets to operate on the original text,
- * but this can be problematic when the layout was calculated on translated text with a
- * different length.
+ * For {@link TextView}, the system does not directly modify the original text, rather
+ * changes the displayed content using a {@link android.text.method.TransformationMethod}. This
+ * can cause issues in apps that do not account for length-changing TransformationMethods. For
+ * example, an app using DynamicLayout may use the calculated line-offsets to operate on the
+ * original text, but this can cause crashes when the layout was calculated on translated text
+ * with a different length.
* <p>
- * If this is {@code true}, for a TextView the default implementation will append spaces to the
- * text to make the length the same as the translated text.
+ * If this is {@code true}, for a TextView the default implementation appends spaces to the
+ * result of {@link TextView#getText()} to make the length the same as the translated text.
+ * <p>
+ * This only affects apps with target SDK R or lower.
*/
public boolean shouldPadContentForCompat() {
return mShouldPadContentForCompat;
@@ -190,19 +193,20 @@
}
/**
- * Whether the original content of the view should be directly modified to include padding that
- * makes it the same size as the translated content. Defaults to {@code false}.
+ * Whether the original content of the view should be made to appear as if it is the same size
+ * as the translated content. Defaults to {@code false}.
* <p>
- * For {@link android.widget.TextView}, the system does not directly modify the original text,
- * rather changes the displayed content using a
- * {@link android.text.method.TransformationMethod}.
- * This can cause issues in apps that do not account for TransformationMethods. For example, an
- * app using DynamicLayout may use the calculated text offsets to operate on the original text,
- * but this can be problematic when the layout was calculated on translated text with a
- * different length.
+ * For {@link TextView}, the system does not directly modify the original text, rather
+ * changes the displayed content using a {@link android.text.method.TransformationMethod}. This
+ * can cause issues in apps that do not account for length-changing TransformationMethods. For
+ * example, an app using DynamicLayout may use the calculated line-offsets to operate on the
+ * original text, but this can cause crashes when the layout was calculated on translated text
+ * with a different length.
* <p>
- * If this is {@code true}, for a TextView the default implementation will append spaces to the
- * text to make the length the same as the translated text.
+ * If this is {@code true}, for a TextView the default implementation appends spaces to the
+ * result of {@link TextView#getText()} to make the length the same as the translated text.
+ * <p>
+ * This only affects apps with target SDK R or lower.
*/
@DataClass.Generated.Member
public @NonNull Builder setShouldPadContentForCompat(boolean value) {
@@ -234,7 +238,7 @@
}
@DataClass.Generated(
- time = 1619034161701L,
+ time = 1620790033058L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/translation/UiTranslationSpec.java",
inputSignatures = "private boolean mShouldPadContentForCompat\npublic boolean shouldPadContentForCompat()\nclass UiTranslationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genToString=true)")
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index a1d4822..0bbaac0f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -60,11 +60,13 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inspector.InspectableProperty;
import android.view.textclassifier.TextClassifier;
+import android.view.translation.TranslationCapability;
import android.view.translation.TranslationSpec.DataFormat;
import android.view.translation.ViewTranslationRequest;
import android.view.translation.ViewTranslationResponse;
@@ -2869,6 +2871,16 @@
}
@Override
+ public void dispatchCreateViewTranslationRequest(@NonNull Map<AutofillId, long[]> viewIds,
+ @NonNull @DataFormat int[] supportedFormats,
+ @Nullable TranslationCapability capability,
+ @NonNull List<ViewTranslationRequest> requests) {
+ super.dispatchCreateViewTranslationRequest(viewIds, supportedFormats, capability, requests);
+ mProvider.getViewDelegate().dispatchCreateViewTranslationRequest(viewIds, supportedFormats,
+ capability, requests);
+ }
+
+ @Override
public void onVirtualViewTranslationResponses(
@NonNull LongSparseArray<ViewTranslationResponse> response) {
mProvider.getViewDelegate().onVirtualViewTranslationResponses(response);
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 8d996ee..f9f823b 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -45,10 +45,12 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.textclassifier.TextClassifier;
+import android.view.translation.TranslationCapability;
import android.view.translation.TranslationSpec.DataFormat;
import android.view.translation.ViewTranslationRequest;
import android.view.translation.ViewTranslationResponse;
@@ -59,6 +61,7 @@
import java.io.BufferedWriter;
import java.io.File;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -377,6 +380,14 @@
LongSparseArray<ViewTranslationResponse> response) {
}
+ default void dispatchCreateViewTranslationRequest(
+ @NonNull @SuppressWarnings("unused") Map<AutofillId, long[]> viewIds,
+ @NonNull @SuppressWarnings("unused") @DataFormat int[] supportedFormats,
+ @Nullable @SuppressWarnings("unused") TranslationCapability capability,
+ @NonNull @SuppressWarnings("unused") List<ViewTranslationRequest> requests) {
+
+ }
+
public AccessibilityNodeProvider getAccessibilityNodeProvider();
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 9eebf06..786e6fc 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -24,7 +24,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.ColorStateList;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.BlendMode;
import android.graphics.Canvas;
@@ -40,8 +39,9 @@
import java.time.Clock;
import java.time.DateTimeException;
+import java.time.Duration;
import java.time.Instant;
-import java.time.LocalDateTime;
+import java.time.LocalTime;
import java.time.ZoneId;
import java.util.Formatter;
import java.util.Locale;
@@ -61,8 +61,8 @@
@Deprecated
public class AnalogClock extends View {
private static final String LOG_TAG = "AnalogClock";
- /** How often the clock should refresh to make the seconds hand advance at ~15 FPS. */
- private static final long SECONDS_TICK_FREQUENCY_MS = 1000 / 15;
+ /** How many times per second that the seconds hand advances. */
+ private static final long SECONDS_HAND_FPS = 30;
private Clock mClock;
@Nullable
@@ -106,7 +106,6 @@
public AnalogClock(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- final Resources r = context.getResources();
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.AnalogClock, defStyleAttr, defStyleRes);
saveAttributeDataForStyleable(context, com.android.internal.R.styleable.AnalogClock,
@@ -716,25 +715,34 @@
}
private void onTimeChanged() {
- long nowMillis = mClock.millis();
- LocalDateTime localDateTime = toLocalDateTime(nowMillis, mClock.getZone());
+ Instant now = mClock.instant();
+ onTimeChanged(now.atZone(mClock.getZone()).toLocalTime(), now.toEpochMilli());
+ }
- int hour = localDateTime.getHour();
- int minute = localDateTime.getMinute();
- int second = localDateTime.getSecond();
+ private void onTimeChanged(LocalTime localTime, long nowMillis) {
+ float previousHour = mHour;
+ float previousMinutes = mMinutes;
- mSeconds = second + localDateTime.getNano() / 1_000_000_000f;
- mMinutes = minute + second / 60.0f;
- mHour = hour + mMinutes / 60.0f;
+ float rawSeconds = localTime.getSecond() + localTime.getNano() / 1_000_000_000f;
+ // We round the fraction of the second so that the seconds hand always occupies the same
+ // n positions between two given numbers, where n is the number of ticks per second. This
+ // ensures the second hand advances by a consistent distance despite our handler callbacks
+ // occurring at inconsistent frequencies.
+ mSeconds = Math.round(rawSeconds * SECONDS_HAND_FPS) / (float) SECONDS_HAND_FPS;
+ mMinutes = localTime.getMinute() + mSeconds / 60.0f;
+ mHour = localTime.getHour() + mMinutes / 60.0f;
mChanged = true;
- updateContentDescription(nowMillis);
+ // Update the content description only if the announced hours and minutes have changed.
+ if ((int) previousHour != (int) mHour || (int) previousMinutes != (int) mMinutes) {
+ updateContentDescription(nowMillis);
+ }
}
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
+ if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
createClock();
}
@@ -747,15 +755,32 @@
private final Runnable mSecondsTick = new Runnable() {
@Override
public void run() {
+ removeCallbacks(this);
if (!mVisible || mSecondHand == null) {
return;
}
- onTimeChanged();
+ Instant now = mClock.instant();
+ LocalTime localTime = now.atZone(mClock.getZone()).toLocalTime();
+ // How many milliseconds through the second we currently are.
+ long millisOfSecond = Duration.ofNanos(localTime.getNano()).toMillis();
+ // How many milliseconds there are between tick positions for the seconds hand.
+ double millisPerTick = 1000 / (double) SECONDS_HAND_FPS;
+ // How many milliseconds we are past the last tick position.
+ long millisPastLastTick = Math.round(millisOfSecond % millisPerTick);
+ // How many milliseconds there are until the next tick position.
+ long millisUntilNextTick = Math.round(millisPerTick - millisPastLastTick);
+ // If we are exactly at the tick position, this could be 0 milliseconds due to rounding.
+ // In this case, advance by the full amount of millis to the next position.
+ if (millisUntilNextTick <= 0) {
+ millisUntilNextTick = Math.round(millisPerTick);
+ }
+ // Schedule a callback for when the next tick should occur.
+ postDelayed(this, millisUntilNextTick);
+
+ onTimeChanged(localTime, now.toEpochMilli());
invalidate();
-
- postDelayed(this, SECONDS_TICK_FREQUENCY_MS);
}
};
@@ -782,14 +807,6 @@
setContentDescription(contentDescription);
}
- private static LocalDateTime toLocalDateTime(long timeMillis, ZoneId zoneId) {
- // java.time types like LocalDateTime / Instant can support the full range of "long millis"
- // with room to spare so we do not need to worry about overflow / underflow and the
- // resulting exceptions while the input to this class is a long.
- Instant instant = Instant.ofEpochMilli(timeMillis);
- return LocalDateTime.ofInstant(instant, zoneId);
- }
-
/**
* Tries to parse a {@link ZoneId} from {@code timeZone}, returning null if it is null or there
* is an error parsing.
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index db67bab..56f81e2 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -11194,7 +11194,7 @@
final long elapsedRealtimeUs = elapsedRealtimeMillis * 1000;
mStartCount = 0;
initTimes(uptimeUs, elapsedRealtimeUs);
- mScreenOnTimer.reset(false, uptimeUs);
+ mScreenOnTimer.reset(false, elapsedRealtimeUs);
mScreenDozeTimer.reset(false, elapsedRealtimeUs);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].reset(false, elapsedRealtimeUs);
diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
index 6e99bbb..c322258 100644
--- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -51,7 +51,7 @@
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
- if (!mHasBluetoothPowerController || !batteryStats.hasBluetoothActivityReporting()) {
+ if (!batteryStats.hasBluetoothActivityReporting()) {
return;
}
@@ -69,8 +69,8 @@
final ControllerActivityCounter activityCounter =
batteryStats.getBluetoothControllerActivity();
final long systemDurationMs = calculateDuration(activityCounter);
- final double systemPowerMah =
- calculatePowerMah(powerModel, measuredChargeUC, activityCounter);
+ final double systemPowerMah = calculatePowerMah(powerModel, measuredChargeUC,
+ activityCounter, query.shouldForceUsePowerProfileModel());
// Subtract what the apps used, but clamp to 0.
final long systemComponentDurationMs = Math.max(0, systemDurationMs - total.durationMs);
@@ -100,7 +100,8 @@
final ControllerActivityCounter activityCounter =
app.getBatteryStatsUid().getBluetoothControllerActivity();
final long durationMs = calculateDuration(activityCounter);
- final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter);
+ final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter,
+ query.shouldForceUsePowerProfileModel());
app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerMah, powerModel);
@@ -132,7 +133,7 @@
batteryStats.getBluetoothControllerActivity();
final long systemDurationMs = calculateDuration(activityCounter);
final double systemPowerMah =
- calculatePowerMah(powerModel, measuredChargeUC, activityCounter);
+ calculatePowerMah(powerModel, measuredChargeUC, activityCounter, false);
// Subtract what the apps used, but clamp to 0.
final double powerMah = Math.max(0, systemPowerMah - total.powerMah);
@@ -165,7 +166,8 @@
final int powerModel = getPowerModel(measuredChargeUC);
final ControllerActivityCounter activityCounter = u.getBluetoothControllerActivity();
final long durationMs = calculateDuration(activityCounter);
- final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter);
+ final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter,
+ false);
app.bluetoothRunningTimeMs = durationMs;
app.bluetoothPowerMah = powerMah;
@@ -188,7 +190,7 @@
/** Returns bluetooth power usage based on the best data available. */
private double calculatePowerMah(@BatteryConsumer.PowerModel int powerModel,
- long measuredChargeUC, ControllerActivityCounter counter) {
+ long measuredChargeUC, ControllerActivityCounter counter, boolean ignoreReportedPower) {
if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
return uCtoMah(measuredChargeUC);
}
@@ -197,12 +199,17 @@
return 0;
}
- final double powerMah =
- counter.getPowerCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
- / (double) (1000 * 60 * 60);
+ if (!ignoreReportedPower) {
+ final double powerMah =
+ counter.getPowerCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
+ / (double) (1000 * 60 * 60);
+ if (powerMah != 0) {
+ return powerMah;
+ }
+ }
- if (powerMah != 0) {
- return powerMah;
+ if (!mHasBluetoothPowerController) {
+ return 0;
}
final long idleTimeMs =
diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
index e670178..50df166 100644
--- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
@@ -477,18 +477,17 @@
}
copyToCurTimes();
boolean notify = false;
- boolean valid = true;
for (int i = 0; i < mFreqCount; i++) {
// Unit is 10ms.
mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
if (mDeltaTimes[i] < 0) {
Slog.e(mTag, "Negative delta from freq time for uid: " + uid
+ ", delta: " + mDeltaTimes[i]);
- valid = false;
+ return;
}
notify |= mDeltaTimes[i] > 0;
}
- if (notify && valid) {
+ if (notify) {
System.arraycopy(mCurTimes, 0, lastTimes, 0, mFreqCount);
if (cb != null) {
cb.onUidCpuTime(uid, mDeltaTimes);
@@ -826,11 +825,11 @@
if (mDeltaTime[i] < 0) {
Slog.e(mTag, "Negative delta from cluster time for uid: " + uid
+ ", delta: " + mDeltaTime[i]);
- valid = false;
+ return;
}
notify |= mDeltaTime[i] > 0;
}
- if (notify && valid) {
+ if (notify) {
System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
if (cb != null) {
cb.onUidCpuTime(uid, mDeltaTime);
diff --git a/core/java/com/android/internal/os/WakelockPowerCalculator.java b/core/java/com/android/internal/os/WakelockPowerCalculator.java
index d594107..e0ef129 100644
--- a/core/java/com/android/internal/os/WakelockPowerCalculator.java
+++ b/core/java/com/android/internal/os/WakelockPowerCalculator.java
@@ -75,22 +75,29 @@
// this remainder to the OS, if possible.
calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs,
BatteryStats.STATS_SINCE_CHARGED, osPowerMah, osDurationMs, totalAppDurationMs);
+ final double remainingPowerMah = result.powerMah;
if (osBatteryConsumer != null) {
osBatteryConsumer.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK,
result.durationMs)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.powerMah);
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, remainingPowerMah);
}
- final long wakeTimeMillis =
- calculateWakeTimeMillis(batteryStats, rawRealtimeUs, rawUptimeUs);
- final double powerMah = mPowerEstimator.calculatePower(wakeTimeMillis);
+ long wakeTimeMs = calculateWakeTimeMillis(batteryStats, rawRealtimeUs, rawUptimeUs);
+ if (wakeTimeMs < 0) {
+ wakeTimeMs = 0;
+ }
builder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
- .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, wakeTimeMillis)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, powerMah);
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK,
+ wakeTimeMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK,
+ appPowerMah + remainingPowerMah);
builder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, appPowerMah);
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK,
+ totalAppDurationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK,
+ appPowerMah);
}
@Override
@@ -167,9 +174,16 @@
}
result.durationMs = osDurationMs + wakeTimeMillis;
result.powerMah = osPowerMah + power;
+ } else {
+ result.durationMs = 0;
+ result.powerMah = 0;
}
}
+ /**
+ * Return on-battery/screen-off time. May be negative if the screen-on time exceeds
+ * the on-battery time.
+ */
private long calculateWakeTimeMillis(BatteryStats batteryStats, long rawRealtimeUs,
long rawUptimeUs) {
final long batteryUptimeUs = batteryStats.getBatteryUptime(rawUptimeUs);
diff --git a/core/res/Android.bp b/core/res/Android.bp
index b988b5a..6063062 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -100,8 +100,7 @@
+ "RES_DIR=$$(dirname $$(dirname $${INPUTS[0]})) && "
+ "mkdir -p $$RES_DIR/values && "
+ "cp $${INPUTS[1]} $$RES_DIR/values && "
- + "$(location soong_zip) -o $(out) -C $$RES_DIR -D $$RES_DIR && "
- + "cp $(out) ."
+ + "$(location soong_zip) -o $(out) -C $$RES_DIR -D $$RES_DIR"
}
android_app {
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index c3b35c8..c4838b8 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -206,6 +206,12 @@
<!-- A tag used to store the margin end for this view when the right icon is gone -->
<item type="id" name="tag_margin_end_when_icon_visible" />
+ <!-- A tag used on the notification @id/left_icon to indicate that this view should be pupulated with the drawable from @id/right_icon when visible. -->
+ <item type="id" name="tag_uses_right_icon_drawable" />
+
+ <!-- A tag used on notification @id/right_icon to indicate that this view should remain visible even when the @id/left_icon is shown. -->
+ <item type="id" name="tag_keep_when_showing_left_icon" />
+
<!-- Marks the "copy to clipboard" button in the ChooserActivity -->
<item type="id" name="chooser_copy_button" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d8620a7..d440173 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3167,6 +3167,8 @@
<java-symbol type="dimen" name="notification_action_disabled_alpha" />
<java-symbol type="id" name="tag_margin_end_when_icon_visible" />
<java-symbol type="id" name="tag_margin_end_when_icon_gone" />
+ <java-symbol type="id" name="tag_uses_right_icon_drawable" />
+ <java-symbol type="id" name="tag_keep_when_showing_left_icon" />
<!-- Override Wake Key Behavior When Screen is Off -->
<java-symbol type="bool" name="config_wakeOnDpadKeyPress" />
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 46dbe0f..e7ee9dc 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -166,7 +166,7 @@
Configuration newConfig = new Configuration();
newConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
- mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null);
+ mResourcesManager.applyConfigurationToResources(newConfig, null);
final Configuration expectedConfig = new Configuration();
expectedConfig.setToDefaults();
diff --git a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
index 4a5528d..79f7a5c 100644
--- a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
@@ -40,6 +40,7 @@
@Test
public void testMeasuredEnergyBasedModel() {
+ mStatsRule.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
stats.updateDisplayMeasuredEnergyStatsLocked(300_000_000, Display.STATE_ON, 0);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index f168b3c..464412f 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -506,6 +506,7 @@
public void testUpdateDisplayMeasuredEnergyStatsLocked() {
final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+ bi.initMeasuredEnergyStats();
clocks.realtime = 0;
int screen = Display.STATE_OFF;
@@ -590,6 +591,7 @@
public void testUpdateCustomMeasuredEnergyStatsLocked_neverCalled() {
final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+ bi.initMeasuredEnergyStats();
bi.setOnBatteryInternal(true);
final int uid1 = 11500;
@@ -603,6 +605,7 @@
public void testUpdateCustomMeasuredEnergyStatsLocked() {
final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+ bi.initMeasuredEnergyStats();
final int bucketA = 0; // Custom bucket 0
final int bucketB = 1; // Custom bucket 1
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
index 1a6408f..8c9591c 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -51,12 +51,7 @@
private final PowerProfile mPowerProfile;
private final MockClocks mMockClocks = new MockClocks();
- private final MockBatteryStatsImpl mBatteryStats = new MockBatteryStatsImpl(mMockClocks) {
- @Override
- public boolean hasBluetoothActivityReporting() {
- return true;
- }
- };
+ private final MockBatteryStatsImpl mBatteryStats;
private BatteryUsageStats mBatteryUsageStats;
private boolean mScreenOn;
@@ -64,6 +59,7 @@
public BatteryUsageStatsRule() {
Context context = InstrumentationRegistry.getContext();
mPowerProfile = spy(new PowerProfile(context, true /* forTest */));
+ mBatteryStats = new MockBatteryStatsImpl(mMockClocks);
mBatteryStats.setPowerProfile(mPowerProfile);
}
@@ -110,10 +106,17 @@
/** Call only after setting the power profile information. */
public BatteryUsageStatsRule initMeasuredEnergyStatsLocked() {
+ return initMeasuredEnergyStatsLocked(new String[0]);
+ }
+
+ /** Call only after setting the power profile information. */
+ public BatteryUsageStatsRule initMeasuredEnergyStatsLocked(
+ String[] customPowerComponentNames) {
final boolean[] supportedStandardBuckets =
new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
Arrays.fill(supportedStandardBuckets, true);
- mBatteryStats.initMeasuredEnergyStatsLocked(supportedStandardBuckets, new String[0]);
+ mBatteryStats.initMeasuredEnergyStatsLocked(supportedStandardBuckets,
+ customPowerComponentNames);
mBatteryStats.informThatAllExternalStatsAreFlushed();
return this;
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
index 2de621c..5c84794 100644
--- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
@@ -22,6 +22,7 @@
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.UidTraffic;
import android.os.BatteryConsumer;
+import android.os.BatteryStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
@@ -43,111 +44,89 @@
.setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE, 10.0)
.setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX, 50.0)
.setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX, 100.0)
- .initMeasuredEnergyStatsLocked();
+ .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE, 3700);
@Test
public void testTimerBasedModel() {
- setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID)
- .getOrCreateBluetoothControllerActivityLocked(),
- 1000, 2000, 3000, 0);
-
- setDurationsAndPower(mStatsRule.getUidStats(APP_UID)
- .getOrCreateBluetoothControllerActivityLocked(),
- 4000, 5000, 6000, 0);
-
- setDurationsAndPower((BatteryStatsImpl.ControllerActivityCounterImpl)
- mStatsRule.getBatteryStats().getBluetoothControllerActivity(),
- 6000, 8000, 10000, 0);
+ setupBluetoothEnergyInfo(0, BatteryStats.POWER_DATA_UNAVAILABLE);
BluetoothPowerCalculator calculator =
new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
- assertBluetoothPowerAndDuration(
- mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
- 0.11388, 6000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- assertBluetoothPowerAndDuration(
- mStatsRule.getUidBatteryConsumer(APP_UID),
- 0.24722, 15000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- assertBluetoothPowerAndDuration(
- mStatsRule.getDeviceBatteryConsumer(),
- 0.40555, 24000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- assertBluetoothPowerAndDuration(
- mStatsRule.getAppsBatteryConsumer(),
- 0.36111, 21000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ assertCalculatedPower(0.08216, 0.18169, 0.26388, 0.26386,
+ BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
- public void testReportedPowerBasedModel() {
- setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID)
- .getOrCreateBluetoothControllerActivityLocked(),
- 1000, 2000, 3000, 360000);
-
- setDurationsAndPower(mStatsRule.getUidStats(APP_UID)
- .getOrCreateBluetoothControllerActivityLocked(),
- 4000, 5000, 6000, 720000);
-
- setDurationsAndPower((BatteryStatsImpl.ControllerActivityCounterImpl)
- mStatsRule.getBatteryStats().getBluetoothControllerActivity(),
- 6000, 8000, 10000, 1260000);
+ public void testReportedEnergyBasedModel() {
+ setupBluetoothEnergyInfo(4000000, BatteryStats.POWER_DATA_UNAVAILABLE);
BluetoothPowerCalculator calculator =
new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
- mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
-
- assertBluetoothPowerAndDuration(
- mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
- 0.1, 6000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- assertBluetoothPowerAndDuration(
- mStatsRule.getUidBatteryConsumer(APP_UID),
- 0.2, 15000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- assertBluetoothPowerAndDuration(
- mStatsRule.getDeviceBatteryConsumer(),
- 0.35, 24000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- assertBluetoothPowerAndDuration(
- mStatsRule.getAppsBatteryConsumer(),
- 0.3, 21000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- }
-
- @Test
- public void testMeasuredEnergyBasedModel() {
- final BluetoothActivityEnergyInfo info = new BluetoothActivityEnergyInfo(1000,
- BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 7000, 5000, 0, 100000);
- info.setUidTraffic(new UidTraffic[]{
- new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
- new UidTraffic(APP_UID, 3000, 4000)
- });
- mStatsRule.getBatteryStats().updateBluetoothStateLocked(info, 1200000, 1000, 1000);
-
- final BluetoothPowerCalculator calculator =
- new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
-
mStatsRule.apply(new BatteryUsageStatsQuery.Builder().includePowerModels().build(),
calculator);
- assertBluetoothPowerAndDuration(
- mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
- 0.10378, 3583, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
- assertBluetoothPowerAndDuration(
- mStatsRule.getUidBatteryConsumer(APP_UID),
- 0.22950, 8416, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
- assertBluetoothPowerAndDuration(
- mStatsRule.getDeviceBatteryConsumer(),
- 0.33333, 12000, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
- assertBluetoothPowerAndDuration(
- mStatsRule.getAppsBatteryConsumer(),
- 0.33329, 11999, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+ assertCalculatedPower(0.08216, 0.18169, 0.30030, 0.26386,
+ BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
- private void setDurationsAndPower(
- BatteryStatsImpl.ControllerActivityCounterImpl controllerActivity, int idleDurationMs,
- int rxDurationMs, int txDurationMs, long powerMaMs) {
- controllerActivity.getIdleTimeCounter().addCountLocked(idleDurationMs);
- controllerActivity.getRxTimeCounter().addCountLocked(rxDurationMs);
- controllerActivity.getTxTimeCounters()[0].addCountLocked(txDurationMs);
- controllerActivity.getPowerCounter().addCountLocked(powerMaMs);
+ @Test
+ public void testMeasuredEnergyBasedModel() {
+ mStatsRule.initMeasuredEnergyStatsLocked();
+ setupBluetoothEnergyInfo(0, 1200000);
+
+ final BluetoothPowerCalculator calculator =
+ new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(calculator);
+
+ assertCalculatedPower(0.10378, 0.22950, 0.33333, 0.33329,
+ BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+ }
+
+ @Test
+ public void testIgnoreMeasuredEnergyBasedModel() {
+ mStatsRule.initMeasuredEnergyStatsLocked();
+ setupBluetoothEnergyInfo(4000000, 1200000);
+
+ BluetoothPowerCalculator calculator =
+ new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
+
+ assertCalculatedPower(0.08216, 0.18169, 0.26388, 0.26386,
+ BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ }
+
+ private void setupBluetoothEnergyInfo(long reportedEnergyUc, long consumedEnergyUc) {
+ final BluetoothActivityEnergyInfo info = new BluetoothActivityEnergyInfo(1000,
+ BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 7000, 5000, 0,
+ reportedEnergyUc);
+ info.setUidTraffic(new UidTraffic[]{
+ new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
+ new UidTraffic(APP_UID, 3000, 4000)
+ });
+ mStatsRule.getBatteryStats().updateBluetoothStateLocked(info,
+ consumedEnergyUc, 1000, 1000);
+ }
+
+ private void assertCalculatedPower(double bluetoothUidPowerMah, double appPowerMah,
+ double devicePowerMah, double allAppsPowerMah, int powerModelPowerProfile) {
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
+ bluetoothUidPowerMah, 3583, powerModelPowerProfile);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getUidBatteryConsumer(APP_UID),
+ appPowerMah, 8416, powerModelPowerProfile);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getDeviceBatteryConsumer(),
+ devicePowerMah, 12000, powerModelPowerProfile);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getAppsBatteryConsumer(),
+ allAppsPowerMah, 11999, powerModelPowerProfile);
}
private void assertBluetoothPowerAndDuration(@Nullable BatteryConsumer batteryConsumer,
@@ -162,7 +141,6 @@
long usageDurationMillis = batteryConsumer.getUsageDurationMillis(
BatteryConsumer.POWER_COMPONENT_BLUETOOTH);
-
assertThat(usageDurationMillis).isEqualTo(durationMs);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
index f8c2bc6..5334f07 100644
--- a/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
@@ -38,7 +38,8 @@
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
@Rule
- public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+ .initMeasuredEnergyStatsLocked(new String[]{"CUSTOM_COMPONENT1", "CUSTOM_COMPONENT2"});
@Test
public void testMeasuredEnergyCopiedIntoBatteryConsumers() {
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 7a7d9f5..80def71 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -48,13 +48,6 @@
setExternalStatsSyncLocked(new DummyExternalStatsSync());
informThatAllExternalStatsAreFlushed();
- final boolean[] supportedStandardBuckets =
- new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
- Arrays.fill(supportedStandardBuckets, true);
- final String[] customBucketNames = {"FOO", "BAR"};
- mGlobalMeasuredEnergyStats =
- new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
-
// A no-op handler.
mHandler = new Handler(Looper.getMainLooper()) {
};
@@ -64,6 +57,15 @@
this(new MockClocks());
}
+ public void initMeasuredEnergyStats() {
+ final boolean[] supportedStandardBuckets =
+ new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
+ Arrays.fill(supportedStandardBuckets, true);
+ final String[] customBucketNames = {"FOO", "BAR"};
+ mGlobalMeasuredEnergyStats =
+ new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
+ }
+
public TimeBase getOnBatteryTimeBase() {
return mOnBatteryTimeBase;
}
diff --git a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
index 4c29c20..c695fc9 100644
--- a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
@@ -47,6 +47,7 @@
@Test
public void testMeasuredEnergyBasedModel() {
+ mStatsRule.initMeasuredEnergyStatsLocked();
BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
batteryStats.noteScreenStateLocked(Display.STATE_ON, 0, 0, 0);
diff --git a/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java
index 82830f2..a7f4fb3 100644
--- a/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java
@@ -79,8 +79,8 @@
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK))
.isWithin(PRECISION).of(0.6);
- BatteryConsumer appConsumer = mStatsRule.getDeviceBatteryConsumer();
+ BatteryConsumer appConsumer = mStatsRule.getAppsBatteryConsumer();
assertThat(appConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK))
- .isWithin(PRECISION).of(0.6);
+ .isWithin(PRECISION).of(0.1);
}
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index e222570..51bf6d53 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -255,6 +255,12 @@
| FILTER_BITMAP_FLAG;
/**
+ * These flags are always set on a reset paint or a new paint instantiated using
+ * {@link #Paint()}.
+ */
+ private static final int DEFAULT_PAINT_FLAGS = ANTI_ALIAS_FLAG | DITHER_FLAG;
+
+ /**
* Font hinter option that disables font hinting.
*
* @see #setHinting(int)
@@ -570,10 +576,13 @@
* accelerated drawing always acts as if {@link #FILTER_BITMAP_FLAG} is set.
* On devices running {@link Build.VERSION_CODES#Q} and above,
* {@code FILTER_BITMAP_FLAG} is set by this constructor, and it can be
- * cleared with {@link #setFlags} or {@link #setFilterBitmap}.</p>
+ * cleared with {@link #setFlags} or {@link #setFilterBitmap}.
+ * On devices running {@link Build.VERSION_CODES#S} and above, {@code ANTI_ALIAS_FLAG} and
+ * {@code DITHER_FLAG} are set by this constructor, and they can be cleared with
+ * {@link #setFlags} or {@link #setAntiAlias} and {@link #setDither}, respectively.</p>
*/
public Paint() {
- this(0);
+ this(DEFAULT_PAINT_FLAGS);
}
/**
@@ -618,7 +627,7 @@
/** Restores the paint to its default settings. */
public void reset() {
nReset(mNativePaint);
- setFlags(HIDDEN_DEFAULT_PAINT_FLAGS);
+ setFlags(HIDDEN_DEFAULT_PAINT_FLAGS | DEFAULT_PAINT_FLAGS);
// TODO: Turning off hinting has undesirable side effects, we need to
// revisit hinting once we add support for subpixel positioning
diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java
index d00492c..6c0981a 100644
--- a/graphics/java/android/graphics/drawable/RippleShader.java
+++ b/graphics/java/android/graphics/drawable/RippleShader.java
@@ -43,10 +43,10 @@
+ "uniform shader in_shader;\n";
private static final String SHADER_LIB =
"float triangleNoise(vec2 n) {\n"
- + " n = fract(n * vec2(5.3987, 5.4421));\n"
- + " n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));\n"
- + " float xy = n.x * n.y;\n"
- + " return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;\n"
+ + " n = fract(n * vec2(5.3987, 5.4421));\n"
+ + " n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));\n"
+ + " float xy = n.x * n.y;\n"
+ + " return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;\n"
+ "}"
+ "const float PI = 3.1415926535897932384626;\n"
+ "\n"
@@ -110,14 +110,16 @@
+ " vec2 uv = p * in_resolutionScale;\n"
+ " vec2 densityUv = uv - mod(uv, in_noiseScale);\n"
+ " float turbulence = turbulence(uv, in_turbulencePhase);\n"
- + " float sparkle = sparkles(densityUv, in_noisePhase) * ring * alpha "
+ + " float sparkleAlpha = sparkles(densityUv, in_noisePhase) * ring * alpha "
+ "* turbulence;\n"
+ " float fade = min(fadeIn, 1. - fadeOutRipple);\n"
- + " float circleAlpha = softCircle(p, center, in_maxRadius * scaleIn, 0.2) * fade;\n"
- + " vec3 color = mix(in_color.rgb, in_sparkleColor.rgb, sparkle);\n"
+ + " float waveAlpha = softCircle(p, center, in_maxRadius * scaleIn, 0.2) * fade "
+ + "* in_color.a;\n"
+ + " vec4 waveColor = vec4(in_color.rgb * waveAlpha, waveAlpha);\n"
+ + " vec4 sparkleColor = vec4(in_sparkleColor.rgb * in_sparkleColor.a, "
+ + "in_sparkleColor.a);\n"
+ " float mask = in_hasMask == 1. ? sample(in_shader, p).a > 0. ? 1. : 0. : 1.;\n"
- + " float a = (in_color.a * circleAlpha + in_sparkleColor.a * sparkle) * mask;\n"
- + " return vec4(color * a, a);\n"
+ + " return mix(waveColor, sparkleColor, sparkleAlpha) * mask;\n"
+ "}";
private static final String SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN;
private static final double PI_ROTATE_RIGHT = Math.PI * 0.0078125;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index ca05ff4..17e0d1b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -208,6 +208,24 @@
}
/**
+ * A handler class that could register itself to apply the transaction instead of the
+ * animation controller doing it. For example, the menu controller can be one such handler.
+ */
+ public static class PipTransactionHandler {
+
+ /**
+ * Called when the animation controller is about to apply a transaction. Allow a registered
+ * handler to apply the transaction instead.
+ *
+ * @return true if handled by the handler, false otherwise.
+ */
+ public boolean handlePipTransaction(SurfaceControl leash, SurfaceControl.Transaction tx,
+ Rect destinationBounds) {
+ return false;
+ }
+ }
+
+ /**
* Animator for PiP transition animation which supports both alpha and bounds animation.
* @param <T> Type of property to animate, either alpha (float) or bounds (Rect)
*/
@@ -225,6 +243,7 @@
private T mEndValue;
private float mStartingAngle;
private PipAnimationCallback mPipAnimationCallback;
+ private PipTransactionHandler mPipTransactionHandler;
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
private PipSurfaceTransactionHelper mSurfaceTransactionHelper;
@@ -293,6 +312,20 @@
mPipAnimationCallback = callback;
return this;
}
+
+ PipTransitionAnimator<T> setPipTransactionHandler(PipTransactionHandler handler) {
+ mPipTransactionHandler = handler;
+ return this;
+ }
+
+ boolean handlePipTransaction(SurfaceControl leash, SurfaceControl.Transaction tx,
+ Rect destinationBounds) {
+ if (mPipTransactionHandler != null) {
+ return mPipTransactionHandler.handlePipTransaction(leash, tx, destinationBounds);
+ }
+ return false;
+ }
+
@VisibleForTesting
@TransitionDirection public int getTransitionDirection() {
return mTransitionDirection;
@@ -499,7 +532,9 @@
getSurfaceTransactionHelper().scaleAndCrop(tx, leash,
initialSourceValue, bounds, insets);
}
- tx.apply();
+ if (!handlePipTransaction(leash, tx, bounds)) {
+ tx.apply();
+ }
}
private void applyRotation(SurfaceControl.Transaction tx, SurfaceControl leash,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index 319d57a..48a15d8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -20,7 +20,6 @@
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.os.SystemProperties;
import android.view.SurfaceControl;
import com.android.wm.shell.R;
@@ -44,10 +43,7 @@
* @param context the current context
*/
public void onDensityOrFontScaleChanged(Context context) {
- final boolean enableCornerRadius =
- SystemProperties.getBoolean("debug.sf.enable_hole_punch_pip", false);
- mCornerRadius = enableCornerRadius
- ? context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius) : 0;
+ mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 4ce6c9e..0633330 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -200,6 +200,19 @@
}
};
+ private final PipAnimationController.PipTransactionHandler mPipTransactionHandler =
+ new PipAnimationController.PipTransactionHandler() {
+ @Override
+ public boolean handlePipTransaction(SurfaceControl leash,
+ SurfaceControl.Transaction tx, Rect destinationBounds) {
+ if (mPipMenuController.isMenuVisible()) {
+ mPipMenuController.movePipMenu(leash, tx, destinationBounds);
+ return true;
+ }
+ return false;
+ }
+ };
+
private ActivityManager.RunningTaskInfo mTaskInfo;
// To handle the edge case that onTaskInfoChanged callback is received during the entering
// PiP transition, where we do not want to intercept the transition but still want to apply the
@@ -433,8 +446,10 @@
// removePipImmediately is expected when the following animation finishes.
ValueAnimator animator = mPipAnimationController
- .getAnimator(mTaskInfo, mLeash, mPipBoundsState.getBounds(), 1f, 0f)
+ .getAnimator(mTaskInfo, mLeash, mPipBoundsState.getBounds(),
+ 1f /* alphaStart */, 0f /* alphaEnd */)
.setTransitionDirection(TRANSITION_DIRECTION_REMOVE_STACK)
+ .setPipTransactionHandler(mPipTransactionHandler)
.setPipAnimationCallback(mPipAnimationCallback);
animator.setDuration(mExitAnimationDuration);
animator.setInterpolator(Interpolators.ALPHA_OUT);
@@ -573,6 +588,7 @@
.getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f)
.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
.setPipAnimationCallback(mPipAnimationCallback)
+ .setPipTransactionHandler(mPipTransactionHandler)
.setDuration(durationMs)
.start();
// mState is set right after the animation is kicked off to block any resize
@@ -749,6 +765,7 @@
mPipAnimationController
.getAnimator(mTaskInfo, mLeash, mPipBoundsState.getBounds(), alphaStart, alphaEnd)
.setTransitionDirection(TRANSITION_DIRECTION_SAME)
+ .setPipTransactionHandler(mPipTransactionHandler)
.setDuration(show ? mEnterAnimationDuration : mExitAnimationDuration)
.start();
mHasFadeOut = !show;
@@ -1226,6 +1243,7 @@
sourceHintRect, direction, startingAngle, rotationDelta);
animator.setTransitionDirection(direction)
.setPipAnimationCallback(mPipAnimationCallback)
+ .setPipTransactionHandler(mPipTransactionHandler)
.setDuration(durationMs)
.start();
if (rotationDelta != Surface.ROTATION_0 && direction == TRANSITION_DIRECTION_TO_PIP) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 9b6909b..4759550 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -111,14 +111,14 @@
final Rect sourceHintRect =
PipBoundsAlgorithm.getValidSourceHintRect(
taskInfo.pictureInPictureParams, currentBounds);
- animator = mPipAnimationController.getAnimator(taskInfo, leash,
- currentBounds, currentBounds, destinationBounds, sourceHintRect,
- TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, Surface.ROTATION_0);
+ animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
+ currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
+ 0 /* startingAngle */, Surface.ROTATION_0);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
t.setAlpha(leash, 0f);
t.apply();
- animator = mPipAnimationController.getAnimator(taskInfo, leash,
- destinationBounds, 0f, 1f);
+ animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
+ 0f, 1f);
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
} else {
throw new RuntimeException("Unrecognized animation type: "
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 9cf0b72..052653e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -70,12 +70,19 @@
*/
public interface Listener {
/**
- * Called when the PIP menu visibility changes.
+ * Called when the PIP menu visibility change has started.
*
- * @param menuState the current state of the menu
+ * @param menuState the new, about-to-change state of the menu
* @param resize whether or not to resize the PiP with the state change
*/
- void onPipMenuStateChanged(int menuState, boolean resize, Runnable callback);
+ void onPipMenuStateChangeStart(int menuState, boolean resize, Runnable callback);
+
+ /**
+ * Called when the PIP menu state has finished changing/animating.
+ *
+ * @param menuState the new state of the menu.
+ */
+ void onPipMenuStateChangeFinish(int menuState);
/**
* Called when the PIP requested to be expanded.
@@ -485,15 +492,15 @@
/**
* Handles changes in menu visibility.
*/
- void onMenuStateChanged(int menuState, boolean resize, Runnable callback) {
+ void onMenuStateChangeStart(int menuState, boolean resize, Runnable callback) {
if (DEBUG) {
- Log.d(TAG, "onMenuStateChanged() mMenuState=" + mMenuState
+ Log.d(TAG, "onMenuStateChangeStart() mMenuState=" + mMenuState
+ " menuState=" + menuState + " resize=" + resize
+ " callers=\n" + Debug.getCallers(5, " "));
}
if (menuState != mMenuState) {
- mListeners.forEach(l -> l.onPipMenuStateChanged(menuState, resize, callback));
+ mListeners.forEach(l -> l.onPipMenuStateChangeStart(menuState, resize, callback));
if (menuState == MENU_STATE_FULL) {
// Once visible, start listening for media action changes. This call will trigger
// the menu actions to be updated again.
@@ -511,6 +518,12 @@
Log.e(TAG, "Unable to update focus as menu appears/disappears", e);
}
}
+ }
+
+ void onMenuStateChangeFinish(int menuState) {
+ if (menuState != mMenuState) {
+ mListeners.forEach(l -> l.onPipMenuStateChangeFinish(menuState));
+ }
mMenuState = menuState;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 29a483b..91e3887 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -535,10 +535,8 @@
private void onPipCornerRadiusChanged() {
if (mPinnedStackAnimationRecentsCallback != null) {
- final boolean enableCornerRadius =
- SystemProperties.getBoolean("debug.sf.enable_hole_punch_pip", false);
- final int cornerRadius = enableCornerRadius
- ? mContext.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius) : 0;
+ final int cornerRadius =
+ mContext.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
try {
mPinnedStackAnimationRecentsCallback.onPipCornerRadiusChanged(cornerRadius);
} catch (RemoteException e) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index ecbf0f1..7b17fe4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -45,7 +45,6 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
@@ -152,11 +151,7 @@
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
inflate(context, R.layout.pip_menu, this);
- final boolean enableCornerRadius =
- SystemProperties.getBoolean("debug.sf.enable_hole_punch_pip", false);
- mBackgroundDrawable = enableCornerRadius
- ? mContext.getDrawable(R.drawable.pip_menu_background)
- : new ColorDrawable(Color.BLACK);
+ mBackgroundDrawable = mContext.getDrawable(R.drawable.pip_menu_background);
mBackgroundDrawable.setAlpha(0);
mViewRoot = findViewById(R.id.background);
mViewRoot.setBackground(mBackgroundDrawable);
@@ -281,17 +276,18 @@
}
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
mMenuContainerAnimator.setDuration(ANIMATION_HIDE_DURATION_MS);
- if (allowMenuTimeout) {
- mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
+ mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ notifyMenuStateChangeFinish(menuState);
+ if (allowMenuTimeout) {
repostDelayedHide(INITIAL_DISMISS_DELAY);
}
- });
- }
+ }
+ });
if (withDelay) {
// starts the menu container animation after window expansion is completed
- notifyMenuStateChange(menuState, resizeMenuOnShow, () -> {
+ notifyMenuStateChangeStart(menuState, resizeMenuOnShow, () -> {
if (mMenuContainerAnimator == null) {
return;
}
@@ -300,11 +296,11 @@
mMenuContainerAnimator.start();
});
} else {
- notifyMenuStateChange(menuState, resizeMenuOnShow, null);
+ notifyMenuStateChangeStart(menuState, resizeMenuOnShow, null);
setVisibility(VISIBLE);
mMenuContainerAnimator.start();
}
- updateActionViews(stackBounds);
+ updateActionViews(menuState, stackBounds);
} else {
// If we are already visible, then just start the delayed dismiss and unregister any
// existing input consumers from the previous drag
@@ -357,7 +353,7 @@
if (mMenuState != MENU_STATE_NONE) {
cancelDelayedHide();
if (notifyMenuVisibility) {
- notifyMenuStateChange(MENU_STATE_NONE, resize, null);
+ notifyMenuStateChangeStart(MENU_STATE_NONE, resize, null);
}
mMenuContainerAnimator = new AnimatorSet();
ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
@@ -376,6 +372,9 @@
@Override
public void onAnimationEnd(Animator animation) {
setVisibility(GONE);
+ if (notifyMenuVisibility) {
+ notifyMenuStateChangeFinish(MENU_STATE_NONE);
+ }
if (animationFinishedRunnable != null) {
animationFinishedRunnable.run();
}
@@ -404,11 +403,11 @@
mActions.clear();
mActions.addAll(actions);
if (mMenuState == MENU_STATE_FULL) {
- updateActionViews(stackBounds);
+ updateActionViews(mMenuState, stackBounds);
}
}
- private void updateActionViews(Rect stackBounds) {
+ private void updateActionViews(int menuState, Rect stackBounds) {
ViewGroup expandContainer = findViewById(R.id.expand_container);
ViewGroup actionsContainer = findViewById(R.id.actions_container);
actionsContainer.setOnTouchListener((v, ev) -> {
@@ -417,13 +416,13 @@
});
// Update the expand button only if it should show with the menu
- expandContainer.setVisibility(mMenuState == MENU_STATE_FULL
+ expandContainer.setVisibility(menuState == MENU_STATE_FULL
? View.VISIBLE
: View.INVISIBLE);
FrameLayout.LayoutParams expandedLp =
(FrameLayout.LayoutParams) expandContainer.getLayoutParams();
- if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE || mMenuState == MENU_STATE_NONE) {
+ if (mActions.isEmpty() || menuState == MENU_STATE_CLOSE || menuState == MENU_STATE_NONE) {
actionsContainer.setVisibility(View.INVISIBLE);
// Update the expand container margin to adjust the center of the expand button to
@@ -493,9 +492,13 @@
expandContainer.requestLayout();
}
- private void notifyMenuStateChange(int menuState, boolean resize, Runnable callback) {
+ private void notifyMenuStateChangeStart(int menuState, boolean resize, Runnable callback) {
+ mController.onMenuStateChangeStart(menuState, resize, callback);
+ }
+
+ private void notifyMenuStateChangeFinish(int menuState) {
mMenuState = menuState;
- mController.onMenuStateChanged(menuState, resize, callback);
+ mController.onMenuStateChangeFinish(menuState);
}
private void expandPip() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 8726ee7..0878f54 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -419,13 +419,13 @@
// Reset the down to begin resizing from this point
mDownPoint.set(mLastPoint);
mDownSecondPoint.set(mLastSecondPoint);
- }
- if (mThresholdCrossed) {
if (mPhonePipMenuController.isMenuVisible()) {
mPhonePipMenuController.hideMenu();
}
+ }
+ if (mThresholdCrossed) {
mAngle = mPinchResizingAlgorithm.calculateBoundsAndAngle(mDownPoint,
mDownSecondPoint, mLastPoint, mLastSecondPoint, mMinSize, mMaxSize,
mDownBounds, mLastResizeBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 0a0798e..8f9dcef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -129,8 +129,13 @@
*/
private class PipMenuListener implements PhonePipMenuController.Listener {
@Override
- public void onPipMenuStateChanged(int menuState, boolean resize, Runnable callback) {
- setMenuState(menuState, resize, callback);
+ public void onPipMenuStateChangeStart(int menuState, boolean resize, Runnable callback) {
+ PipTouchHandler.this.onPipMenuStateChangeStart(menuState, resize, callback);
+ }
+
+ @Override
+ public void onPipMenuStateChangeFinish(int menuState) {
+ setMenuState(menuState);
}
@Override
@@ -614,7 +619,7 @@
}
}
- shouldDeliverToMenu |= !mPipBoundsState.isStashed();
+ shouldDeliverToMenu &= !mPipBoundsState.isStashed();
// Deliver the event to PipMenuActivity to handle button click if the menu has shown.
if (shouldDeliverToMenu) {
@@ -646,9 +651,9 @@
}
/**
- * Sets the menu visibility.
+ * Called when the PiP menu state is in the process of animating/changing from one to another.
*/
- private void setMenuState(int menuState, boolean resize, Runnable callback) {
+ private void onPipMenuStateChangeStart(int menuState, boolean resize, Runnable callback) {
if (mMenuState == menuState && !resize) {
return;
}
@@ -686,6 +691,9 @@
mSavedSnapFraction = -1f;
}
}
+ }
+
+ private void setMenuState(int menuState) {
mMenuState = menuState;
updateMovementBounds();
// If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index fd6f0ad9..1f098c2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -66,7 +66,7 @@
// For example, an icon with the foreground 108*108 opaque pixels and it's background
// also 108*108 pixels, then do not enlarge this icon if only need to show foreground icon.
private static final float ENLARGE_FOREGROUND_ICON_THRESHOLD = (72f * 72f) / (108f * 108f);
- private static final float NO_BACKGROUND_SCALE = 1.3f;
+ private static final float NO_BACKGROUND_SCALE = 192f / 160;
private final Context mContext;
private final IconProvider mIconProvider;
@@ -283,7 +283,8 @@
} else {
final float iconScale = (float) mIconSize / (float) mDefaultIconSize;
final int densityDpi = mContext.getResources().getDisplayMetrics().densityDpi;
- final int scaledIconDpi = (int) (0.5f + iconScale * densityDpi);
+ final int scaledIconDpi =
+ (int) (0.5f + iconScale * densityDpi * NO_BACKGROUND_SCALE);
iconDrawable = mIconProvider.getIcon(mActivityInfo, scaledIconDpi);
if (iconDrawable == null) {
iconDrawable = mContext.getPackageManager().getDefaultActivityIcon();
@@ -356,7 +357,7 @@
Slog.d(TAG, "makeSplashScreenContentView: choose fg icon");
}
// Reference AdaptiveIcon description, outer is 108 and inner is 72, so we
- // should enlarge the size 108/72 if we only draw adaptiveIcon's foreground.
+ // scale by 192/160 if we only draw adaptiveIcon's foreground.
final float noBgScale =
foreIconTester.nonTransparentRatio() < ENLARGE_FOREGROUND_ICON_THRESHOLD
? NO_BACKGROUND_SCALE : 1f;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index a1e6832..26e8753 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -103,7 +103,7 @@
private Choreographer mChoreographer;
private static final boolean DEBUG_ENABLE_REVEAL_ANIMATION =
- SystemProperties.getBoolean("persist.debug.enable_reveal_animation", false);
+ SystemProperties.getBoolean("persist.debug.enable_reveal_animation", true);
/**
* @param splashScreenExecutor The thread used to control add and remove starting window.
*/
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index 1f9ff4ab..b5198bb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -18,7 +18,6 @@
import android.os.SystemClock
import android.platform.test.annotations.Presubmit
-import android.provider.Settings
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -27,6 +26,8 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.wm.shell.flicker.appPairsDividerIsInvisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import org.junit.After
import org.junit.Before
import org.junit.FixMethodOrder
@@ -49,7 +50,6 @@
class AppPairsTestCannotPairNonResizeableApps(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
- var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
@@ -64,21 +64,15 @@
}
@Before
- fun setup() {
- prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
- if (prevSupportNonResizableInMultiWindow == 1) {
- // Not support non-resizable in multi window
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0)
- }
+ override fun setup() {
+ super.setup()
+ setSupportsNonResizableMultiWindow(instrumentation, -1)
}
@After
- fun teardown() {
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
- prevSupportNonResizableInMultiWindow)
+ override fun teardown() {
+ super.teardown()
+ resetMultiWindowConfig(instrumentation)
}
@FlakyTest
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
index 1e3595c..f2a375b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -18,7 +18,6 @@
import android.os.SystemClock
import android.platform.test.annotations.Presubmit
-import android.provider.Settings
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -27,6 +26,8 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.wm.shell.flicker.appPairsDividerIsVisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import org.junit.After
import org.junit.Before
import org.junit.FixMethodOrder
@@ -49,7 +50,6 @@
class AppPairsTestSupportPairNonResizeableApps(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
- var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
@@ -64,21 +64,15 @@
}
@Before
- fun setup() {
- prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
- if (prevSupportNonResizableInMultiWindow == 0) {
- // Support non-resizable in multi window
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1)
- }
+ override fun setup() {
+ super.setup()
+ setSupportsNonResizableMultiWindow(instrumentation, 1)
}
@After
- fun teardown() {
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
- prevSupportNonResizableInMultiWindow)
+ override fun teardown() {
+ super.teardown()
+ resetMultiWindowConfig(instrumentation)
}
@FlakyTest
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 741773e..1935bb9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -20,11 +20,9 @@
import android.content.Context
import android.platform.test.annotations.Presubmit
import android.system.helpers.ActivityHelper
-import android.util.Log
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.compatibility.common.util.SystemUtil
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
@@ -41,10 +39,14 @@
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.BaseAppHelper
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEnableNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.testapp.Components
+import org.junit.After
+import org.junit.Before
import org.junit.Test
-import java.io.IOException
abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
@@ -62,6 +64,21 @@
protected var primaryTaskId = ""
protected var secondaryTaskId = ""
protected var nonResizeableTaskId = ""
+ private var prevDevEnableNonResizableMultiWindow = 0
+
+ @Before
+ open fun setup() {
+ prevDevEnableNonResizableMultiWindow = getDevEnableNonResizableMultiWindow(context)
+ if (prevDevEnableNonResizableMultiWindow != 0) {
+ // Turn off the development option
+ setDevEnableNonResizableMultiWindow(context, 0)
+ }
+ }
+
+ @After
+ open fun teardown() {
+ setDevEnableNonResizableMultiWindow(context, prevDevEnableNonResizableMultiWindow)
+ }
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
@@ -117,11 +134,7 @@
}
internal fun executeShellCommand(cmd: String) {
- try {
- SystemUtil.runShellCommand(instrumentation, cmd)
- } catch (e: IOException) {
- Log.d("AppPairsTest", "executeShellCommand error! $e")
- }
+ BaseAppHelper.executeShellCommand(instrumentation, cmd)
}
internal fun composePairsCommand(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
index 006b569..4fe69ad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
@@ -21,11 +21,14 @@
import android.content.pm.PackageManager.FEATURE_LEANBACK
import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY
import android.support.test.launcherhelper.LauncherStrategyFactory
+import android.util.Log
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
+import com.android.compatibility.common.util.SystemUtil
import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.traces.parser.toWindowName
+import java.io.IOException
abstract class BaseAppHelper(
instrumentation: Instrumentation,
@@ -56,5 +59,13 @@
companion object {
private const val APP_CLOSE_WAIT_TIME_MS = 3_000L
+
+ fun executeShellCommand(instrumentation: Instrumentation, cmd: String) {
+ try {
+ SystemUtil.runShellCommand(instrumentation, cmd)
+ } catch (e: IOException) {
+ Log.e("BaseAppHelper", "executeShellCommand error! $e")
+ }
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
new file mode 100644
index 0000000..7f99e62
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.helpers
+
+import android.app.Instrumentation
+import android.content.ComponentName
+import android.content.Context
+import android.provider.Settings
+
+class MultiWindowHelper(
+ instrumentation: Instrumentation,
+ activityLabel: String,
+ componentsInfo: ComponentName
+) : BaseAppHelper(instrumentation, activityLabel, componentsInfo) {
+
+ companion object {
+ fun getDevEnableNonResizableMultiWindow(context: Context): Int =
+ Settings.Global.getInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
+
+ fun setDevEnableNonResizableMultiWindow(context: Context, configValue: Int) =
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, configValue)
+
+ fun setSupportsNonResizableMultiWindow(instrumentation: Instrumentation, configValue: Int) =
+ executeShellCommand(
+ instrumentation,
+ createConfigSupportsNonResizableMultiWindowCommand(configValue))
+
+ fun resetMultiWindowConfig(instrumentation: Instrumentation) =
+ executeShellCommand(instrumentation, resetMultiWindowConfigCommand)
+
+ private fun createConfigSupportsNonResizableMultiWindowCommand(configValue: Int): String =
+ "wm set-multi-window-config --supportsNonResizable $configValue"
+
+ private const val resetMultiWindowConfigCommand: String = "wm reset-multi-window-config"
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
index 04f97c8..c18c122 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.platform.test.annotations.Presubmit
-import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -27,6 +26,8 @@
import com.android.server.wm.flicker.helpers.canSplitScreen
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.After
import org.junit.Assert
@@ -51,7 +52,6 @@
class EnterSplitScreenNotSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
@@ -76,21 +76,15 @@
splitScreenApp.defaultWindowName)
@Before
- fun setup() {
- prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
- if (prevSupportNonResizableInMultiWindow == 1) {
- // Not support non-resizable in multi window
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0)
- }
+ override fun setup() {
+ super.setup()
+ setSupportsNonResizableMultiWindow(instrumentation, -1)
}
@After
- fun teardown() {
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
- prevSupportNonResizableInMultiWindow)
+ override fun teardown() {
+ super.teardown()
+ resetMultiWindowConfig(instrumentation)
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
index 2832bb4..d5b9a13 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.platform.test.annotations.Presubmit
-import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -27,6 +26,8 @@
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.After
import org.junit.Before
@@ -50,7 +51,6 @@
class EnterSplitScreenSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
@@ -73,21 +73,15 @@
splitScreenApp.defaultWindowName)
@Before
- fun setup() {
- prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
- if (prevSupportNonResizableInMultiWindow != 1) {
- // Support non-resizable in multi window
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1)
- }
+ override fun setup() {
+ super.setup()
+ setSupportsNonResizableMultiWindow(instrumentation, 1)
}
@After
- fun teardown() {
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
- prevSupportNonResizableInMultiWindow)
+ override fun teardown() {
+ super.teardown()
+ resetMultiWindowConfig(instrumentation)
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
index 32afd19..612018e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.platform.test.annotations.Presubmit
-import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -32,6 +31,8 @@
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.After
import org.junit.Before
@@ -53,7 +54,6 @@
class LegacySplitScreenFromIntentNotSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
@@ -77,21 +77,15 @@
WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
@Before
- fun setup() {
- prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
- if (prevSupportNonResizableInMultiWindow == 1) {
- // Not support non-resizable in multi window
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0)
- }
+ override fun setup() {
+ super.setup()
+ setSupportsNonResizableMultiWindow(instrumentation, -1)
}
@After
- fun teardown() {
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
- prevSupportNonResizableInMultiWindow)
+ override fun teardown() {
+ super.teardown()
+ resetMultiWindowConfig(instrumentation)
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
index af30758..65062f9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.platform.test.annotations.Presubmit
-import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -30,6 +29,8 @@
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.After
import org.junit.Before
@@ -51,7 +52,6 @@
class LegacySplitScreenFromIntentSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
@@ -75,21 +75,15 @@
WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
@Before
- fun setup() {
- prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
- if (prevSupportNonResizableInMultiWindow == 0) {
- // Support non-resizable in multi window
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1)
- }
+ override fun setup() {
+ super.setup()
+ setSupportsNonResizableMultiWindow(instrumentation, 1)
}
@After
- fun teardown() {
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
- prevSupportNonResizableInMultiWindow)
+ override fun teardown() {
+ super.teardown()
+ resetMultiWindowConfig(instrumentation)
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
index 8c62758..3720787 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.platform.test.annotations.Presubmit
-import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -33,6 +32,8 @@
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.After
import org.junit.Before
@@ -54,7 +55,6 @@
class LegacySplitScreenFromRecentNotSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
@@ -78,21 +78,15 @@
WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
@Before
- fun setup() {
- prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
- if (prevSupportNonResizableInMultiWindow == 1) {
- // Not support non-resizable in multi window
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0)
- }
+ override fun setup() {
+ super.setup()
+ setSupportsNonResizableMultiWindow(instrumentation, -1)
}
@After
- fun teardown() {
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
- prevSupportNonResizableInMultiWindow)
+ override fun teardown() {
+ super.teardown()
+ resetMultiWindowConfig(instrumentation)
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
index 5b48f8a..61ebcd2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.platform.test.annotations.Presubmit
-import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -31,6 +30,8 @@
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.After
import org.junit.Before
@@ -52,7 +53,6 @@
class LegacySplitScreenFromRecentSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
@@ -76,21 +76,15 @@
WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
@Before
- fun setup() {
- prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
- if (prevSupportNonResizableInMultiWindow == 0) {
- // Support non-resizable in multi window
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1)
- }
+ override fun setup() {
+ super.setup()
+ setSupportsNonResizableMultiWindow(instrumentation, 1)
}
@After
- fun teardown() {
- Settings.Global.putInt(context.contentResolver,
- Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
- prevSupportNonResizableInMultiWindow)
+ override fun teardown() {
+ super.teardown()
+ resetMultiWindowConfig(instrumentation)
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
index 8684ba5..e8d4d1e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
@@ -32,7 +32,11 @@
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
+import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEnableNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.After
+import org.junit.Before
import org.junit.Test
abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestParameter) {
@@ -44,6 +48,21 @@
protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation)
protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation)
.launcherStrategy.supportedLauncherPackage
+ private var prevDevEnableNonResizableMultiWindow = 0
+
+ @Before
+ open fun setup() {
+ prevDevEnableNonResizableMultiWindow = getDevEnableNonResizableMultiWindow(context)
+ if (prevDevEnableNonResizableMultiWindow != 0) {
+ // Turn off the development option
+ setDevEnableNonResizableMultiWindow(context, 0)
+ }
+ }
+
+ @After
+ open fun teardown() {
+ setDevEnableNonResizableMultiWindow(context, prevDevEnableNonResizableMultiWindow)
+ }
/**
* List of windows that are ignored when verifying that visible elements appear on 2
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index a164233..0adc0f6 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -157,7 +157,7 @@
static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
if (in.isEmpty()) return;
SkRect temp(in);
- if (Properties::stretchEffectBehavior == StretchEffectBehavior::LinearScale) {
+ if (Properties::stretchEffectBehavior == StretchEffectBehavior::UniformScale) {
const StretchEffect& stretch = props.layerProperties().getStretchEffect();
if (!stretch.isEmpty()) {
applyMatrix(stretch.makeLinearStretch(props.getWidth(), props.getHeight()), &temp);
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 9ed801b..a4614a9 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -138,7 +138,7 @@
if (targetCpuTimePercentage <= 0 || targetCpuTimePercentage > 100) targetCpuTimePercentage = 70;
int stretchType = base::GetIntProperty(PROPERTY_STRETCH_EFFECT_TYPE, 0);
- stretchType = std::clamp(stretchType, 0, static_cast<int>(StretchEffectBehavior::LinearScale));
+ stretchType = std::clamp(stretchType, 0, static_cast<int>(StretchEffectBehavior::UniformScale));
stretchEffectBehavior = static_cast<StretchEffectBehavior>(stretchType);
return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 9d2b617..9964254 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -200,9 +200,9 @@
enum class RenderPipelineType { SkiaGL, SkiaVulkan, NotInitialized = 128 };
enum class StretchEffectBehavior {
- ShaderHWUI, // Stretch shader in HWUI only, matrix scale in SF
- Shader, // Stretch shader in both HWUI and SF
- LinearScale // Linear stretch everywhere
+ ShaderHWUI, // Stretch shader in HWUI only, matrix scale in SF
+ Shader, // Stretch shader in both HWUI and SF
+ UniformScale // Uniform scale stretch everywhere
};
/**
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 64abd94..ad2cd8c 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -478,7 +478,7 @@
}
}
- if (Properties::stretchEffectBehavior == StretchEffectBehavior::LinearScale) {
+ if (Properties::stretchEffectBehavior == StretchEffectBehavior::UniformScale) {
const StretchEffect& stretch = properties().layerProperties().getStretchEffect();
if (!stretch.isEmpty()) {
matrix.multiply(
diff --git a/libs/hwui/effects/StretchEffect.h b/libs/hwui/effects/StretchEffect.h
index 3b3067d..0e1a654 100644
--- a/libs/hwui/effects/StretchEffect.h
+++ b/libs/hwui/effects/StretchEffect.h
@@ -111,7 +111,7 @@
bool requiresLayer() const {
return !(isEmpty() ||
- Properties::stretchEffectBehavior == StretchEffectBehavior::LinearScale);
+ Properties::stretchEffectBehavior == StretchEffectBehavior::UniformScale);
}
private:
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index cd0783f..a096ed0 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -573,8 +573,8 @@
const RenderProperties& props = node.properties();
uirenderer::Rect bounds(props.getWidth(), props.getHeight());
- bool useStretchShader = Properties::stretchEffectBehavior !=
- StretchEffectBehavior::LinearScale;
+ bool useStretchShader =
+ Properties::stretchEffectBehavior != StretchEffectBehavior::UniformScale;
if (useStretchShader && info.stretchEffectCount) {
handleStretchEffect(info, bounds);
}
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 0b43f09..1c5515c7d 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -248,7 +248,7 @@
const StretchEffect& stretch = properties.layerProperties().getStretchEffect();
if (stretch.isEmpty() ||
- Properties::stretchEffectBehavior == StretchEffectBehavior::LinearScale) {
+ Properties::stretchEffectBehavior == StretchEffectBehavior::UniformScale) {
// If we don't have any stretch effects, issue the filtered
// canvas draw calls to make sure we still punch a hole
// with the same canvas transformation + clip into the target
@@ -327,7 +327,7 @@
canvas->concat(*properties.getTransformMatrix());
}
}
- if (Properties::stretchEffectBehavior == StretchEffectBehavior::LinearScale) {
+ if (Properties::stretchEffectBehavior == StretchEffectBehavior::UniformScale) {
const StretchEffect& stretch = properties.layerProperties().getStretchEffect();
if (!stretch.isEmpty()) {
canvas->concat(
diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
index 0b995bc..1042703 100644
--- a/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
+++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
@@ -86,12 +86,9 @@
}
}
- // if we don't have a resource name then we don't know how to label the
- // data and should abort.
+ // if we don't have a pretty name then use the dumpName
if (resourceName == nullptr) {
- mCurrentElement.clear();
- mCurrentValues.clear();
- return;
+ resourceName = mCurrentElement.c_str();
}
auto result = mResults.find(resourceName);
@@ -157,6 +154,14 @@
}
}
+size_t SkiaMemoryTracer::total() {
+ processElement();
+ if (!strcmp("bytes", mTotalSize.units)) {
+ return mTotalSize.value;
+ }
+ return 0;
+}
+
void SkiaMemoryTracer::logTotals(String8& log) {
TraceValue total = convertUnits(mTotalSize);
TraceValue purgeable = convertUnits(mPurgeableSize);
diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.h b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
index b393b07..cba3b04 100644
--- a/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
+++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
@@ -37,6 +37,7 @@
bool hasOutput();
void logOutput(String8& log);
void logTotals(String8& log);
+ size_t total();
void dumpNumericValue(const char* dumpName, const char* valueName, const char* units,
uint64_t value) override;
diff --git a/libs/hwui/pipeline/skia/StretchMask.cpp b/libs/hwui/pipeline/skia/StretchMask.cpp
index 2bbd8a4..1c58c6a 100644
--- a/libs/hwui/pipeline/skia/StretchMask.cpp
+++ b/libs/hwui/pipeline/skia/StretchMask.cpp
@@ -46,9 +46,16 @@
if (mIsDirty) {
SkCanvas* maskCanvas = mMaskSurface->getCanvas();
+ // Make sure to apply target transformation to the mask canvas
+ // to ensure the replayed drawing commands generate the same result
+ auto previousMatrix = displayList->mParentMatrix;
+ displayList->mParentMatrix = maskCanvas->getTotalMatrix();
+ maskCanvas->save();
maskCanvas->drawColor(0, SkBlendMode::kClear);
TransformCanvas transformCanvas(maskCanvas, SkBlendMode::kSrcOver);
displayList->draw(&transformCanvas);
+ maskCanvas->restore();
+ displayList->mParentMatrix = previousMatrix;
}
sk_sp<SkImage> maskImage = mMaskSurface->makeImageSnapshot();
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 5047be9..46e8060 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -130,27 +130,43 @@
mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30));
}
+void CacheManager::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
+ *cpuUsage = 0;
+ *gpuUsage = 0;
+ if (!mGrContext) {
+ return;
+ }
+
+ skiapipeline::SkiaMemoryTracer cpuTracer("category", true);
+ SkGraphics::DumpMemoryStatistics(&cpuTracer);
+ *cpuUsage += cpuTracer.total();
+
+ skiapipeline::SkiaMemoryTracer gpuTracer("category", true);
+ mGrContext->dumpMemoryStatistics(&gpuTracer);
+ *gpuUsage += gpuTracer.total();
+}
+
void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) {
if (!mGrContext) {
log.appendFormat("No valid cache instance.\n");
return;
}
- log.appendFormat("Font Cache (CPU):\n");
- log.appendFormat(" Size: %.2f kB \n", SkGraphics::GetFontCacheUsed() / 1024.0f);
- log.appendFormat(" Glyph Count: %d \n", SkGraphics::GetFontCacheCountUsed());
-
std::vector<skiapipeline::ResourcePair> cpuResourceMap = {
{"skia/sk_resource_cache/bitmap_", "Bitmaps"},
{"skia/sk_resource_cache/rrect-blur_", "Masks"},
{"skia/sk_resource_cache/rects-blur_", "Masks"},
{"skia/sk_resource_cache/tessellated", "Shadows"},
+ {"skia/sk_glyph_cache", "Glyph Cache"},
};
skiapipeline::SkiaMemoryTracer cpuTracer(cpuResourceMap, false);
SkGraphics::DumpMemoryStatistics(&cpuTracer);
if (cpuTracer.hasOutput()) {
log.appendFormat("CPU Caches:\n");
cpuTracer.logOutput(log);
+ log.appendFormat(" Glyph Count: %d \n", SkGraphics::GetFontCacheCountUsed());
+ log.appendFormat("Total CPU memory usage:\n");
+ cpuTracer.logTotals(log);
}
skiapipeline::SkiaMemoryTracer gpuTracer("category", true);
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index 0a6b8dc..713ea99 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -47,6 +47,7 @@
void trimMemory(TrimMemoryMode mode);
void trimStaleResources();
void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr);
+ void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage);
size_t getCacheSize() const { return mMaxResourceBytes; }
size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; }
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index ad325cf..95aa29d 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -195,6 +195,17 @@
}
}
+void RenderProxy::purgeCaches() {
+ if (RenderThread::hasInstance()) {
+ RenderThread& thread = RenderThread::getInstance();
+ thread.queue().post([&thread]() {
+ if (thread.getGrContext()) {
+ thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+ }
+ });
+ }
+}
+
void RenderProxy::overrideProperty(const char* name, const char* value) {
// expensive, but block here since name/value pointers owned by caller
RenderThread::getInstance().queue().runSync(
@@ -256,6 +267,13 @@
}
}
+void RenderProxy::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
+ if (RenderThread::hasInstance()) {
+ auto& thread = RenderThread::getInstance();
+ thread.queue().runSync([&]() { thread.getMemoryUsage(cpuUsage, gpuUsage); });
+ }
+}
+
void RenderProxy::setProcessStatsBuffer(int fd) {
auto& rt = RenderThread::getInstance();
rt.queue().post([&rt, fd = dup(fd)]() {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 662b445..0681dc5 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -98,6 +98,7 @@
void destroyHardwareResources();
static void trimMemory(int level);
+ static void purgeCaches();
static void overrideProperty(const char* name, const char* value);
void fence();
@@ -110,6 +111,7 @@
void resetProfileInfo();
uint32_t frameTimePercentile(int p);
static void dumpGraphicsMemory(int fd, bool includeProfileData = true);
+ static void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage);
static void rotateProcessStatsBuffer();
static void setProcessStatsBuffer(int fd);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 308352d..0268bfd7 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -323,6 +323,10 @@
dprintf(fd, "\nPipeline=%s\n%s\n", pipelineToString(), cachesOutput.string());
}
+void RenderThread::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
+ mCacheManager->getMemoryUsage(cpuUsage, gpuUsage);
+}
+
Readback& RenderThread::readback() {
if (!mReadback) {
mReadback = new Readback(*this);
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index afd5750..5021085 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -151,6 +151,7 @@
sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& skBitmap);
void dumpGraphicsMemory(int fd, bool includeProfileData);
+ void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage);
void requireGlContext();
void requireVkContext();
diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h
index 781884a..6b0be53 100644
--- a/libs/hwui/tests/common/TestScene.h
+++ b/libs/hwui/tests/common/TestScene.h
@@ -40,6 +40,7 @@
int reportFrametimeWeight = 0;
bool renderOffscreen = true;
bool reportGpuMemoryUsage = false;
+ bool reportGpuMemoryUsageVerbose = false;
};
template <class T>
diff --git a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
index 964b8bf..c451112 100644
--- a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
@@ -22,8 +22,10 @@
class StretchyListViewAnimation;
class StretchyListViewHolePunch;
-class StretchyLinearListView;
-class StretchyLinearListViewHolePunch;
+class StretchyUniformListView;
+class StretchyUniformListViewHolePunch;
+class StretchyUniformLayerListView;
+class StretchyUniformLayerListViewHolePunch;
static TestScene::Registrar _StretchyListViewAnimation(TestScene::Info{
"stretchylistview",
@@ -36,21 +38,34 @@
"A mock ListView of scrolling content that's stretching. Includes a hole punch",
TestScene::simpleCreateScene<StretchyListViewHolePunch>});
-static TestScene::Registrar _StretchyLinearListView(TestScene::Info{
- "stretchylistview_linear",
- "A mock ListView of scrolling content that's stretching using a linear stretch effect.",
- TestScene::simpleCreateScene<StretchyLinearListView>});
+static TestScene::Registrar _StretchyUniformListView(TestScene::Info{
+ "stretchylistview_uniform",
+ "A mock ListView of scrolling content that's stretching using a uniform stretch effect.",
+ TestScene::simpleCreateScene<StretchyUniformListView>});
-static TestScene::Registrar _StretchyLinearListViewHolePunch(TestScene::Info{
- "stretchylistview_linear_holepunch",
- "A mock ListView of scrolling content that's stretching using a linear stretch effect. "
+static TestScene::Registrar _StretchyUniformListViewHolePunch(TestScene::Info{
+ "stretchylistview_uniform_holepunch",
+ "A mock ListView of scrolling content that's stretching using a uniform stretch effect. "
"Includes a hole punch",
- TestScene::simpleCreateScene<StretchyLinearListViewHolePunch>});
+ TestScene::simpleCreateScene<StretchyUniformListViewHolePunch>});
+
+static TestScene::Registrar _StretchyUniformLayerListView(TestScene::Info{
+ "stretchylistview_uniform_layer",
+ "A mock ListView of scrolling content that's stretching using a uniform stretch effect. "
+ "Uses a layer",
+ TestScene::simpleCreateScene<StretchyUniformLayerListView>});
+
+static TestScene::Registrar _StretchyUniformLayerListViewHolePunch(TestScene::Info{
+ "stretchylistview_uniform_layer_holepunch",
+ "A mock ListView of scrolling content that's stretching using a uniform stretch effect. "
+ "Uses a layer & includes a hole punch",
+ TestScene::simpleCreateScene<StretchyUniformLayerListViewHolePunch>});
class StretchyListViewAnimation : public TestScene {
protected:
virtual StretchEffectBehavior stretchBehavior() { return StretchEffectBehavior::Shader; }
virtual bool haveHolePunch() { return false; }
+ virtual bool forceLayer() { return false; }
private:
int mItemHeight;
@@ -172,6 +187,10 @@
void doFrame(int frameNr) override {
if (frameNr == 0) {
Properties::stretchEffectBehavior = stretchBehavior();
+ if (forceLayer()) {
+ mListView->mutateStagingProperties().mutateLayerProperties().setType(
+ LayerType::RenderLayer);
+ }
}
auto& props = mListView->mutateStagingProperties();
auto& stretch = props.mutateLayerProperties().mutableStretchEffect();
@@ -190,11 +209,22 @@
bool haveHolePunch() override { return true; }
};
-class StretchyLinearListView : public StretchyListViewAnimation {
- StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::LinearScale; }
+class StretchyUniformListView : public StretchyListViewAnimation {
+ StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::UniformScale; }
};
-class StretchyLinearListViewHolePunch : public StretchyListViewAnimation {
- StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::LinearScale; }
+class StretchyUniformListViewHolePunch : public StretchyListViewAnimation {
+ StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::UniformScale; }
bool haveHolePunch() override { return true; }
+};
+
+class StretchyUniformLayerListView : public StretchyListViewAnimation {
+ StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::UniformScale; }
+ bool forceLayer() override { return true; }
+};
+
+class StretchyUniformLayerListViewHolePunch : public StretchyListViewAnimation {
+ StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::UniformScale; }
+ bool haveHolePunch() override { return true; }
+ bool forceLayer() override { return true; }
};
\ No newline at end of file
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 9d3b732..b640b90 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -28,6 +28,20 @@
#include <log/log.h>
#include <ui/PixelFormat.h>
+// These are unstable internal APIs in google-benchmark. We should just implement our own variant
+// of these instead, but this was quicker. Disabled-by-default to avoid any breakages when
+// google-benchmark updates if they change anything
+#if 0
+#define USE_SKETCHY_INTERNAL_STATS
+namespace benchmark {
+std::vector<BenchmarkReporter::Run> ComputeStats(
+ const std::vector<BenchmarkReporter::Run> &reports);
+double StatisticsMean(const std::vector<double>& v);
+double StatisticsMedian(const std::vector<double>& v);
+double StatisticsStdDev(const std::vector<double>& v);
+}
+#endif
+
using namespace android;
using namespace android::uirenderer;
using namespace android::uirenderer::renderthread;
@@ -66,6 +80,7 @@
void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options& opts,
double durationInS, int repetationIndex, BenchmarkResults* reports) {
+ using namespace benchmark;
benchmark::BenchmarkReporter::Run report;
report.repetitions = opts.repeatCount;
report.repetition_index = repetationIndex;
@@ -73,12 +88,22 @@
report.iterations = static_cast<int64_t>(opts.frameCount);
report.real_accumulated_time = durationInS;
report.cpu_accumulated_time = durationInS;
- report.counters["items_per_second"] = opts.frameCount / durationInS;
+ report.counters["FPS"] = opts.frameCount / durationInS;
+ if (opts.reportGpuMemoryUsage) {
+ size_t cpuUsage, gpuUsage;
+ RenderProxy::getMemoryUsage(&cpuUsage, &gpuUsage);
+ report.counters["Rendering RAM"] = Counter{static_cast<double>(cpuUsage + gpuUsage),
+ Counter::kDefaults, Counter::kIs1024};
+ }
reports->push_back(report);
}
static void doRun(const TestScene::Info& info, const TestScene::Options& opts, int repetitionIndex,
BenchmarkResults* reports) {
+ if (opts.reportGpuMemoryUsage) {
+ // If we're reporting GPU memory usage we need to first start with a clean slate
+ RenderProxy::purgeCaches();
+ }
Properties::forceDrawFrame = true;
TestContext testContext;
testContext.setRenderOffscreen(opts.renderOffscreen);
@@ -162,11 +187,6 @@
void run(const TestScene::Info& info, const TestScene::Options& opts,
benchmark::BenchmarkReporter* reporter) {
- if (opts.reportGpuMemoryUsage) {
- // If we're reporting GPU memory usage we need to first start with a clean slate
- // All repetitions of the same test will share a single memory usage report
- RenderProxy::trimMemory(100);
- }
BenchmarkResults results;
for (int i = 0; i < opts.repeatCount; i++) {
doRun(info, opts, i, reporter ? &results : nullptr);
@@ -174,10 +194,21 @@
if (reporter) {
reporter->ReportRuns(results);
if (results.size() > 1) {
- // TODO: Report summary
+#ifdef USE_SKETCHY_INTERNAL_STATS
+ std::vector<benchmark::internal::Statistics> stats;
+ stats.reserve(3);
+ stats.emplace_back("mean", benchmark::StatisticsMean);
+ stats.emplace_back("median", benchmark::StatisticsMedian);
+ stats.emplace_back("stddev", benchmark::StatisticsStdDev);
+ for (auto& it : results) {
+ it.statistics = &stats;
+ }
+ auto summary = benchmark::ComputeStats(results);
+ reporter->ReportRuns(summary);
+#endif
}
}
- if (opts.reportGpuMemoryUsage) {
+ if (opts.reportGpuMemoryUsageVerbose) {
RenderProxy::dumpGraphicsMemory(STDOUT_FILENO, false);
}
}
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index e9e962a..f3f32eb 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -71,7 +71,7 @@
--benchmark_format Set output format. Possible values are tabular, json, csv
--renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk
--skip-leak-check Skips the memory leak check
- --report-gpu-memory Dumps the GPU memory usage after each test run
+ --report-gpu-memory[=verbose] Dumps the GPU memory usage after each test run
)");
}
@@ -142,7 +142,7 @@
} else if (!strcmp(format, "json")) {
gBenchmarkReporter.reset(new benchmark::JSONReporter());
} else {
- fprintf(stderr, "Unknown format '%s'", format);
+ fprintf(stderr, "Unknown format '%s'\n", format);
return false;
}
return true;
@@ -154,7 +154,7 @@
} else if (!strcmp(renderer, "skiavk")) {
Properties::overrideRenderPipelineType(RenderPipelineType::SkiaVulkan);
} else {
- fprintf(stderr, "Unknown format '%s'", renderer);
+ fprintf(stderr, "Unknown format '%s'\n", renderer);
return false;
}
return true;
@@ -191,7 +191,7 @@
{"offscreen", no_argument, nullptr, LongOpts::Offscreen},
{"renderer", required_argument, nullptr, LongOpts::Renderer},
{"skip-leak-check", no_argument, nullptr, LongOpts::SkipLeakCheck},
- {"report-gpu-memory", no_argument, nullptr, LongOpts::ReportGpuMemory},
+ {"report-gpu-memory", optional_argument, nullptr, LongOpts::ReportGpuMemory},
{0, 0, 0, 0}};
static const char* SHORT_OPTIONS = "c:r:h";
@@ -296,6 +296,14 @@
case LongOpts::ReportGpuMemory:
gOpts.reportGpuMemoryUsage = true;
+ if (optarg) {
+ if (!strcmp("verbose", optarg)) {
+ gOpts.reportGpuMemoryUsageVerbose = true;
+ } else {
+ fprintf(stderr, "Invalid report gpu memory option '%s'\n", optarg);
+ error = true;
+ }
+ }
break;
case 'h':
@@ -313,7 +321,7 @@
}
if (error) {
- fprintf(stderr, "Try 'hwuitest --help' for more information.\n");
+ fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
exit(EXIT_FAILURE);
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 3d6cc68..2e4d8f8 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -139,19 +139,13 @@
*/
private String mRequestorPackageName;
- /**
- * Indicates what fields should be redacted from this instance.
- */
- private final @RedactionType long mRedactions;
-
public NetworkCapabilities() {
- mRedactions = REDACT_ALL;
clearAll();
mNetworkCapabilities = DEFAULT_CAPABILITIES;
}
public NetworkCapabilities(NetworkCapabilities nc) {
- this(nc, REDACT_ALL);
+ this(nc, REDACT_NONE);
}
/**
@@ -163,10 +157,12 @@
* @hide
*/
public NetworkCapabilities(@Nullable NetworkCapabilities nc, @RedactionType long redactions) {
- mRedactions = redactions;
if (nc != null) {
set(nc);
}
+ if (mTransportInfo != null) {
+ mTransportInfo = nc.mTransportInfo.makeCopy(redactions);
+ }
}
/**
@@ -175,14 +171,6 @@
* @hide
*/
public void clearAll() {
- // Ensures that the internal copies maintained by the connectivity stack does not set it to
- // anything other than |REDACT_ALL|.
- if (mRedactions != REDACT_ALL) {
- // This is needed because the current redaction mechanism relies on redaction while
- // parceling.
- throw new UnsupportedOperationException(
- "Cannot clear NetworkCapabilities when mRedactions is set");
- }
mNetworkCapabilities = mTransportTypes = mForbiddenNetworkCapabilities = 0;
mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
mNetworkSpecifier = null;
@@ -211,7 +199,7 @@
mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
mNetworkSpecifier = nc.mNetworkSpecifier;
if (nc.getTransportInfo() != null) {
- setTransportInfo(nc.getTransportInfo().makeCopy(mRedactions));
+ setTransportInfo(nc.getTransportInfo());
} else {
setTransportInfo(null);
}
@@ -839,8 +827,17 @@
final int[] originalAdministratorUids = getAdministratorUids();
final TransportInfo originalTransportInfo = getTransportInfo();
clearAll();
- mTransportTypes = (originalTransportTypes & TEST_NETWORKS_ALLOWED_TRANSPORTS)
- | (1 << TRANSPORT_TEST);
+ if (0 != (originalCapabilities & NET_CAPABILITY_NOT_RESTRICTED)) {
+ // If the test network is not restricted, then it is only allowed to declare some
+ // specific transports. This is to minimize impact on running apps in case an app
+ // run from the shell creates a test a network.
+ mTransportTypes =
+ (originalTransportTypes & UNRESTRICTED_TEST_NETWORKS_ALLOWED_TRANSPORTS)
+ | (1 << TRANSPORT_TEST);
+ } else {
+ // If the test transport is restricted, then it may declare any transport.
+ mTransportTypes = (originalTransportTypes | (1 << TRANSPORT_TEST));
+ }
mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
mNetworkSpecifier = originalSpecifier;
mSignalStrength = originalSignalStrength;
@@ -951,9 +948,10 @@
};
/**
- * Allowed transports on a test network, in addition to TRANSPORT_TEST.
+ * Allowed transports on an unrestricted test network (in addition to TRANSPORT_TEST).
*/
- private static final int TEST_NETWORKS_ALLOWED_TRANSPORTS = 1 << TRANSPORT_TEST
+ private static final int UNRESTRICTED_TEST_NETWORKS_ALLOWED_TRANSPORTS =
+ 1 << TRANSPORT_TEST
// Test ethernet networks can be created with EthernetManager#setIncludeTestInterfaces
| 1 << TRANSPORT_ETHERNET
// Test VPN networks can be created but their UID ranges must be empty.
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 16a946d..f8cb5d3 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -561,7 +561,20 @@
break;
}
- Log.d(TAG, "status=" + statusString + ", cause=" + causeString + ", detail=" + detail);
+ StringBuilder msg = new StringBuilder();
+ msg.append("status: " + statusString + ", cause: " + causeString);
+ if (status == STATUS_IN_PROGRESS) {
+ msg.append(
+ String.format(
+ ", partition name: %s, progress: %d/%d",
+ mCurrentPartitionName,
+ mCurrentPartitionInstalledSize,
+ mCurrentPartitionSize));
+ }
+ if (detail != null) {
+ msg.append(", detail: " + detail);
+ }
+ Log.d(TAG, msg.toString());
if (notifyOnNotificationBar) {
mNM.notify(NOTIFICATION_ID, buildNotification(status, cause, detail));
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 59ea9f0..f18d426 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -320,20 +320,21 @@
}
}
- private void installScratch() throws IOException {
- final long scratchSize = mDynSystem.suggestScratchSize();
+ private void installWritablePartition(final String partitionName, final long partitionSize)
+ throws IOException {
+ Log.d(TAG, "Creating writable partition: " + partitionName + ", size: " + partitionSize);
+
Thread thread = new Thread() {
@Override
public void run() {
mInstallationSession =
- mDynSystem.createPartition("scratch", scratchSize, /* readOnly= */ false);
+ mDynSystem.createPartition(
+ partitionName, partitionSize, /* readOnly= */ false);
}
};
- Log.d(TAG, "Creating partition: scratch, size = " + scratchSize);
thread.start();
-
- Progress progress = new Progress("scratch", scratchSize, mNumInstalledPartitions++);
+ Progress progress = new Progress(partitionName, partitionSize, mNumInstalledPartitions++);
while (thread.isAlive()) {
if (isCancelled()) {
@@ -356,53 +357,22 @@
if (mInstallationSession == null) {
throw new IOException(
- "Failed to start installation with requested size: " + scratchSize);
+ "Failed to start installation with requested size: " + partitionSize);
}
+
// Reset installation session and verify that installation completes successfully.
mInstallationSession = null;
if (!mDynSystem.closePartition()) {
- throw new IOException("Failed to complete partition installation: scratch");
+ throw new IOException("Failed to complete partition installation: " + partitionName);
}
}
+ private void installScratch() throws IOException {
+ installWritablePartition("scratch", mDynSystem.suggestScratchSize());
+ }
+
private void installUserdata() throws IOException {
- Thread thread = new Thread(() -> {
- mInstallationSession = mDynSystem.createPartition("userdata", mUserdataSize, false);
- });
-
- Log.d(TAG, "Creating partition: userdata, size = " + mUserdataSize);
- thread.start();
-
- Progress progress = new Progress("userdata", mUserdataSize, mNumInstalledPartitions++);
-
- while (thread.isAlive()) {
- if (isCancelled()) {
- return;
- }
-
- final long installedSize = mDynSystem.getInstallationProgress().bytes_processed;
-
- if (installedSize > progress.installedSize + MIN_PROGRESS_TO_PUBLISH) {
- progress.installedSize = installedSize;
- publishProgress(progress);
- }
-
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- // Ignore the error.
- }
- }
-
- if (mInstallationSession == null) {
- throw new IOException(
- "Failed to start installation with requested size: " + mUserdataSize);
- }
- // Reset installation session and verify that installation completes successfully.
- mInstallationSession = null;
- if (!mDynSystem.closePartition()) {
- throw new IOException("Failed to complete partition installation: userdata");
- }
+ installWritablePartition("userdata", mUserdataSize);
}
private void installImages() throws IOException, ImageValidationException {
diff --git a/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm b/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
index 44d5a0c..1614188 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
@@ -171,15 +171,15 @@
}
key LEFT_BRACKET {
- label: '['
+ label: ']'
base, capslock: '\u062c'
- shift: '<'
+ shift: '>'
}
key RIGHT_BRACKET {
- label: ']'
+ label: '['
base, capslock: '\u062f'
- shift: '>'
+ shift: '<'
}
key BACKSLASH {
diff --git a/packages/SettingsLib/FooterPreference/Android.bp b/packages/SettingsLib/FooterPreference/Android.bp
index 11f39e7..0929706 100644
--- a/packages/SettingsLib/FooterPreference/Android.bp
+++ b/packages/SettingsLib/FooterPreference/Android.bp
@@ -20,4 +20,8 @@
],
sdk_version: "system_current",
min_sdk_version: "21",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ ],
}
diff --git a/packages/SettingsLib/RadioButtonPreference/Android.bp b/packages/SettingsLib/RadioButtonPreference/Android.bp
index b309c01..28ff71f 100644
--- a/packages/SettingsLib/RadioButtonPreference/Android.bp
+++ b/packages/SettingsLib/RadioButtonPreference/Android.bp
@@ -20,4 +20,8 @@
sdk_version: "system_current",
min_sdk_version: "21",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ ],
}
diff --git a/packages/SettingsLib/TwoTargetPreference/Android.bp b/packages/SettingsLib/TwoTargetPreference/Android.bp
index 078e8c3..b32d5b4 100644
--- a/packages/SettingsLib/TwoTargetPreference/Android.bp
+++ b/packages/SettingsLib/TwoTargetPreference/Android.bp
@@ -19,4 +19,8 @@
],
sdk_version: "system_current",
min_sdk_version: "21",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ ],
}
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_0.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_0.xml
new file mode 100644
index 0000000..efae569
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_0.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.66,7C18.19,5.07 15.14,4 12,4C8.58,4 5.27,5.27 2.7,7.53L12,18.85l6,-7.3v3.15L12,22L0,7.39C2.97,4.08 7.25,2 12,2c4.56,0 8.69,1.92 11.64,5H20.66z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,10h-2v8h2V10z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_1.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_1.xml
new file mode 100644
index 0000000..d50e734
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_1.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M23.62,7C20.65,3.93 16.6,2 12,2C7.2,2 3,4.1 0,7.4L12,22l6,-7.3v-3.19l-2.13,2.59C14.74,13.4 13.39,13 12,13s-2.74,0.4 -3.87,1.1L2.7,7.5C5.3,5.3 8.6,4 12,4c3.13,0 6.18,1.1 8.68,3H23.62z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,10h-2v8h2V10z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_2.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_2.xml
new file mode 100644
index 0000000..1be297e
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_2.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M23.62,7C20.65,3.93 16.6,2 12,2C7.2,2 3,4.1 0,7.4L12,22l6,-7.3v-3.19l-0.27,0.33C16.12,10.67 14.09,10 12,10c-2.09,0 -4.12,0.67 -5.73,1.84L2.7,7.5C5.3,5.3 8.6,4 12,4c3.13,0 6.18,1.1 8.68,3H23.62z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,10h-2v8h2V10z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_3.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_3.xml
new file mode 100644
index 0000000..738bd5a
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_3.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M0,7.4L12,22l6,-7.3V8.57C16.21,7.56 14.14,7 12,7C9.2,7 6.53,7.96 4.45,9.62L2.7,7.5C5.3,5.3 8.6,4 12,4c3.13,0 6.18,1.1 8.68,3h2.95C20.65,3.93 16.6,2 12,2C7.2,2 3,4.1 0,7.4z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,10h-2v8h2V10z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_4.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_4.xml
new file mode 100644
index 0000000..14d020b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_4.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M0,7.4C3,4.1 7.2,2 12,2c4.6,0 8.65,1.93 11.62,5H18v7.7L12,22L0,7.4z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,10h-2v8h2V10z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
index 9889419..4dd3ff1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
@@ -187,8 +187,9 @@
}
protected int getIconColorAttr() {
- return (mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED)
- ? android.R.attr.colorAccent : android.R.attr.colorControlNormal;
+ final boolean accent = (mWifiEntry.hasInternetAccess()
+ && mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED);
+ return accent ? android.R.attr.colorAccent : android.R.attr.colorControlNormal;
}
private void updateIcon(boolean showX, int level) {
@@ -267,7 +268,7 @@
}
public Drawable getIcon(boolean showX, int level) {
- return mContext.getDrawable(Utils.getWifiIconResource(showX, level));
+ return mContext.getDrawable(WifiUtils.getInternetIconResource(level, showX));
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 15b146d..6100615 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -36,6 +36,22 @@
private static final int INVALID_RSSI = -127;
+ static final int[] WIFI_PIE = {
+ com.android.internal.R.drawable.ic_wifi_signal_0,
+ com.android.internal.R.drawable.ic_wifi_signal_1,
+ com.android.internal.R.drawable.ic_wifi_signal_2,
+ com.android.internal.R.drawable.ic_wifi_signal_3,
+ com.android.internal.R.drawable.ic_wifi_signal_4
+ };
+
+ static final int[] NO_INTERNET_WIFI_PIE = {
+ R.drawable.ic_no_internet_wifi_signal_0,
+ R.drawable.ic_no_internet_wifi_signal_1,
+ R.drawable.ic_no_internet_wifi_signal_2,
+ R.drawable.ic_no_internet_wifi_signal_3,
+ R.drawable.ic_no_internet_wifi_signal_4
+ };
+
public static String buildLoggingSummary(AccessPoint accessPoint, WifiConfiguration config) {
final StringBuilder summary = new StringBuilder();
final WifiInfo info = accessPoint.getInfo();
@@ -245,6 +261,20 @@
return context.getString(R.string.wifi_unmetered_label);
}
+ /**
+ * Returns the Internet icon resource for a given RSSI level.
+ *
+ * @param level The number of bars to show (0-4)
+ * @param noInternet True if a connected Wi-Fi network cannot access the Internet
+ * @throws IllegalArgumentException if an invalid RSSI level is given.
+ */
+ public static int getInternetIconResource(int level, boolean noInternet) {
+ if (level < 0 || level >= WIFI_PIE.length) {
+ throw new IllegalArgumentException("No Wifi icon found for level: " + level);
+ }
+ return noInternet ? NO_INTERNET_WIFI_PIE[level] : WIFI_PIE[level];
+ }
+
public static boolean isMeteredOverridden(WifiConfiguration config) {
return config.meteredOverride != WifiConfiguration.METERED_OVERRIDE_NONE;
}
diff --git a/packages/SystemUI/res/drawable/ic_conversation_icon.xml b/packages/SystemUI/res/drawable/ic_conversation_icon.xml
new file mode 100644
index 0000000..0e3533b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_conversation_icon.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+ <path
+ android:pathData="M24,24m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"
+ android:fillColor="#81C995"/>
+ <path
+ android:pathData="M27,34C23.134,34 20,30.866 20,27C20,23.134 23.134,20 27,20C30.866,20 34,23.134 34,27C34,28.4872 33.5362,29.8662 32.7453,31"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#3C4043"/>
+ <path
+ android:pathData="M35,33l-8,0l-0,2l8,0z"
+ android:fillColor="#3C4043"/>
+ <path
+ android:pathData="M21,21m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"
+ android:fillColor="#81C995"/>
+ <path
+ android:pathData="M16,25h5v2h-5z"
+ android:fillColor="#81C995"/>
+ <path
+ android:pathData="M21,28C24.866,28 28,24.866 28,21C28,17.134 24.866,14 21,14C17.134,14 14,17.134 14,21C14,22.4872 14.4638,23.8662 15.2547,25"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#ffffff"/>
+ <path
+ android:pathData="M13,27h8v2h-8z"
+ android:fillColor="#ffffff"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_corp_badge_off.xml b/packages/SystemUI/res/drawable/ic_corp_badge_off.xml
new file mode 100644
index 0000000..a441fb2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_corp_badge_off.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="20.0"
+ android:viewportHeight="20.0">
+ <path
+ android:pathData="M10,10m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
+ android:fillColor="#607D8B"/>
+ <path
+ android:pathData="M16.42,15.68l-0.85,-0.85L7.21,6.47L4.9,4.16L4.16,4.9l1.57,1.57H5.36c-0.65,0 -1.16,0.52 -1.16,1.17L4.2,14.05c0,0.65 0.52,1.17 1.17,1.17h9.12l1.2,1.2L16.42,15.68zM15.83,7.64c0.03,-0.65 -0.49,-1.17 -1.14,-1.14h-2.33V5.3c0,-0.65 -0.52,-1.17 -1.17,-1.14H8.86C8.22,4.14 7.7,4.66 7.7,5.3v0.19l8.14,8.17V7.64zM11.2,6.5H8.83V5.3h2.36V6.5z"
+ android:fillColor="#FFFFFF"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/people_tile_empty_background.xml b/packages/SystemUI/res/drawable/people_tile_empty_background.xml
new file mode 100644
index 0000000..2dac740
--- /dev/null
+++ b/packages/SystemUI/res/drawable/people_tile_empty_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <solid android:color="?androidprv:attr/colorSurface" />
+ <corners android:radius="@dimen/people_space_widget_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/people_tile_status_scrim.xml b/packages/SystemUI/res/drawable/people_tile_status_scrim.xml
new file mode 100644
index 0000000..cf16f1c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/people_tile_status_scrim.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2021 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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <gradient
+ android:type="linear"
+ android:angle="90"
+ android:endColor="@android:color/transparent"
+ android:startColor="?androidprv:attr/colorSurfaceHeader" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/people_tile_suppressed_background.xml b/packages/SystemUI/res/drawable/people_tile_suppressed_background.xml
new file mode 100644
index 0000000..e0c111d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/people_tile_suppressed_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <solid android:color="?androidprv:attr/colorSurfaceVariant" />
+ <corners android:radius="@dimen/people_space_widget_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_call_chip.xml b/packages/SystemUI/res/layout/ongoing_call_chip.xml
index a146547..90214b7 100644
--- a/packages/SystemUI/res/layout/ongoing_call_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_call_chip.xml
@@ -23,6 +23,7 @@
android:background="@drawable/ongoing_call_chip_bg"
android:paddingStart="@dimen/ongoing_call_chip_side_padding"
android:paddingEnd="@dimen/ongoing_call_chip_side_padding"
+ android:contentDescription="@string/ongoing_phone_call_content_description"
>
<ImageView
diff --git a/packages/SystemUI/res/layout/people_status_scrim_layout.xml b/packages/SystemUI/res/layout/people_status_scrim_layout.xml
new file mode 100644
index 0000000..9808f74
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_status_scrim_layout.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+~ Copyright (C) 2021 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/scrim_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/status_icon"
+ android:scaleType="centerCrop"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:weightSum="1"
+ android:orientation="vertical">
+ <ImageView
+ android:layout_weight=".2"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+ <ImageView
+ android:background="@drawable/people_tile_status_scrim"
+ android:layout_weight=".8"
+ android:scaleType="centerCrop"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:weightSum="1"
+ android:orientation="vertical">
+ <ImageView
+ android:layout_weight=".66"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+ <ImageView
+ android:background="@drawable/people_tile_status_scrim"
+ android:layout_weight=".33"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+ </LinearLayout>
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/people_tile_empty_layout.xml b/packages/SystemUI/res/layout/people_tile_empty_layout.xml
index 7d3b919..8e9ebc6 100644
--- a/packages/SystemUI/res/layout/people_tile_empty_layout.xml
+++ b/packages/SystemUI/res/layout/people_tile_empty_layout.xml
@@ -14,16 +14,17 @@
~ limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/item"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
- android:background="@drawable/people_space_tile_view_card"
+ android:background="@drawable/people_tile_empty_background"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
- android:id="@+id/item"
+ android:id="@+id/icon"
+ android:src="@drawable/ic_conversation_icon"
android:gravity="center"
android:layout_gravity="center"
- android:padding="8dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:layout_width="48dp"
+ android:layout_height="48dp"/>
</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_large_with_content.xml b/packages/SystemUI/res/layout/people_tile_large_with_content.xml
index 6f8de3b..b77670e 100644
--- a/packages/SystemUI/res/layout/people_tile_large_with_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_large_with_content.xml
@@ -1,145 +1,159 @@
<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright (C) 2021 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.
- -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:theme="@android:style/Theme.DeviceDefault.DayNight"
- android:id="@+id/item"
+~ Copyright (C) 2021 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@drawable/people_space_tile_view_card"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:id="@+id/item"
+ android:clipToOutline="true"
+ android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:layout_gravity="center"
- android:padding="16dp"
- android:orientation="vertical">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
- <RelativeLayout
+ <include layout="@layout/people_status_scrim_layout" />
+
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="start|top">
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:padding="16dp"
+ android:orientation="vertical">
- <LinearLayout
- android:layout_width="wrap_content"
+ <RelativeLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:gravity="start|top"
- android:orientation="horizontal">
+ android:gravity="start|top">
- <ImageView
- android:id="@+id/person_icon"
- android:layout_marginStart="-2dp"
- android:layout_marginTop="-2dp"
+ <LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="1" />
+ android:layout_alignParentStart="true"
+ android:gravity="start|top"
+ android:orientation="horizontal">
- <ImageView
- android:id="@+id/availability"
- android:layout_marginStart="-2dp"
- android:layout_width="10dp"
- android:layout_height="10dp"
- android:background="@drawable/circle_green_10dp" />
- </LinearLayout>
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_marginStart="-2dp"
+ android:layout_marginTop="-2dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
- <TextView
- android:id="@+id/messages_count"
- android:layout_alignParentEnd="true"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorPrimary"
- android:background="@drawable/people_space_messages_count_background"
- android:textSize="14sp"
- android:maxLines="1"
- android:ellipsize="end"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- />
- </RelativeLayout>
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <include layout="@layout/people_tile_punctuation_background_large" />
- <include layout="@layout/people_tile_emoji_background_large" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <TextView
- android:layout_gravity="center"
- android:id="@+id/name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingBottom="12dp"
- android:gravity="start"
- android:singleLine="true"
- android:ellipsize="end"
- android:text="@string/empty_user_name"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp" />
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingBottom="4dp"
- android:gravity="center_vertical"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/predefined_icon"
- android:tint="?android:attr/colorAccent"
- android:gravity="start|center_vertical"
- android:paddingEnd="6dp"
- android:layout_width="24dp"
- android:layout_height="18dp" />
+ <ImageView
+ android:id="@+id/availability"
+ android:layout_marginStart="-2dp"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="@drawable/circle_green_10dp" />
+ </LinearLayout>
<TextView
- android:layout_gravity="center"
- android:id="@+id/subtext"
- android:gravity="center_vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:singleLine="true"
- android:text="@string/empty_user_name"
+ android:id="@+id/messages_count"
+ android:layout_alignParentEnd="true"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="12sp" />
- </LinearLayout>
+ android:textColor="?android:attr/textColorPrimary"
+ android:background="@drawable/people_space_messages_count_background"
+ android:textSize="14sp"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+ </RelativeLayout>
- <ImageView
- android:id="@+id/image"
+ <RelativeLayout
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/people_space_content_background"
- android:gravity="center"
- android:scaleType="centerCrop" />
+ android:layout_height="match_parent">
- <TextView
- android:layout_gravity="center"
- android:id="@+id/text_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="2"
- android:singleLine="false"
- android:text="@string/empty_status"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="12sp" />
- </LinearLayout>
- </RelativeLayout>
-</LinearLayout>
+ <include layout="@layout/people_tile_punctuation_background_large" />
+
+ <include layout="@layout/people_tile_emoji_background_large" />
+
+ <LinearLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_gravity="center"
+ android:id="@+id/name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="12dp"
+ android:gravity="start"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:text="@string/empty_user_name"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp" />
+
+ <LinearLayout
+ android:id="@+id/status_icon_and_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="4dp"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/predefined_icon"
+ android:tint="?android:attr/colorAccent"
+ android:gravity="start|center_vertical"
+ android:paddingEnd="6dp"
+ android:layout_width="24dp"
+ android:layout_height="18dp" />
+
+ <TextView
+ android:layout_gravity="center"
+ android:id="@+id/subtext"
+ android:gravity="center_vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:text="@string/empty_user_name"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="12sp" />
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/people_space_content_background"
+ android:gravity="center"
+ android:scaleType="centerCrop" />
+
+ <TextView
+ android:layout_gravity="center"
+ android:id="@+id/text_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:singleLine="false"
+ android:text="@string/empty_status"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/content_text_size_for_large" />
+ </LinearLayout>
+ </RelativeLayout>
+ </LinearLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
index a8c15ab..8df0bf8 100644
--- a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
@@ -17,18 +17,20 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
+ android:id="@+id/item"
+ android:background="@drawable/people_space_tile_view_card"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
- android:background="@drawable/people_space_tile_view_card"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/people_tile_punctuation_background_medium" />
- <include layout="@layout/people_tile_emoji_background_medium" />
+ <include layout="@layout/people_tile_punctuation_background_medium" />
+ <include layout="@layout/people_status_scrim_layout" />
<LinearLayout
- android:id="@+id/item"
+ android:id="@+id/content"
android:orientation="vertical"
android:layout_gravity="center"
android:padding="8dp"
@@ -89,7 +91,7 @@
android:text="@string/empty_status"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:textColor="?android:attr/textColorPrimary"
- android:textSize="12sp"
+ android:textSize="@dimen/content_text_size_for_medium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="2"
diff --git a/packages/SystemUI/res/layout/people_tile_suppressed_layout.xml b/packages/SystemUI/res/layout/people_tile_suppressed_layout.xml
new file mode 100644
index 0000000..b151c60
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_tile_suppressed_layout.xml
@@ -0,0 +1,29 @@
+<!--
+ ~ Copyright (C) 2021 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.
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/item"
+ android:theme="@android:style/Theme.DeviceDefault.DayNight"
+ android:background="@drawable/people_tile_suppressed_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:gravity="center"
+ android:layout_gravity="center"
+ android:layout_width="48dp"
+ android:layout_height="48dp"/>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_work_profile_quiet_layout.xml b/packages/SystemUI/res/layout/people_tile_work_profile_quiet_layout.xml
new file mode 100644
index 0000000..25ab5a6
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_tile_work_profile_quiet_layout.xml
@@ -0,0 +1,39 @@
+<!--
+ ~ Copyright (C) 2021 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.
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/item"
+ android:theme="@android:style/Theme.DeviceDefault.DayNight"
+ android:background="@drawable/people_tile_suppressed_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:gravity="center"
+ android:layout_gravity="center"
+ android:layout_width="48dp"
+ android:layout_height="48dp"/>
+
+ <ImageView android:id="@+id/work_widget_badge_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|right"
+ android:layout_marginBottom="12dp"
+ android:layout_marginRight="12dp"
+ android:src="@drawable/ic_corp_badge_off"
+ android:clickable="false" />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index f056402..59e1a75 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -23,7 +23,6 @@
android:clickable="true"
android:orientation="vertical"
android:layout_marginTop="@*android:dimen/quick_qs_offset_height"
- android:layout_marginBottom="@dimen/qs_container_bottom_padding"
android:paddingBottom="8dp"
android:visibility="invisible"
android:elevation="4dp"
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 4607e5f..4c6418a 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -17,7 +17,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/quick_settings_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:clipToPadding="false"
android:clipChildren="false" >
@@ -25,7 +25,6 @@
android:id="@+id/expanded_qs_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingBottom="@dimen/qs_container_bottom_padding"
android:elevation="4dp"
android:importantForAccessibility="no"
android:scrollbars="none"
diff --git a/packages/SystemUI/res/layout/qs_tile_side_icon.xml b/packages/SystemUI/res/layout/qs_tile_side_icon.xml
index 9f9af9d..1ae0a1c 100644
--- a/packages/SystemUI/res/layout/qs_tile_side_icon.xml
+++ b/packages/SystemUI/res/layout/qs_tile_side_icon.xml
@@ -35,6 +35,7 @@
android:layout_width="@dimen/qs_icon_size"
android:layout_height="@dimen/qs_icon_size"
android:src="@*android:drawable/ic_chevron_end"
+ android:autoMirrored="true"
android:visibility="gone"
android:importantForAccessibility="no"
/>
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index a62310b..c88703d 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -28,7 +28,7 @@
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
android:minWidth="48dp"
android:minHeight="48dp"
@@ -61,9 +61,9 @@
<LinearLayout
android:id="@+id/rightLayout"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:gravity="center_vertical|end"
>
<com.android.systemui.statusbar.phone.StatusIconContainer
android:id="@+id/statusIcons"
diff --git a/packages/SystemUI/res/layout/quick_settings_security_footer.xml b/packages/SystemUI/res/layout/quick_settings_security_footer.xml
index ce7f827..08bd71c 100644
--- a/packages/SystemUI/res/layout/quick_settings_security_footer.xml
+++ b/packages/SystemUI/res/layout/quick_settings_security_footer.xml
@@ -55,6 +55,7 @@
android:layout_marginStart="8dp"
android:contentDescription="@null"
android:src="@*android:drawable/ic_chevron_end"
+ android:autoMirrored="true"
android:tint="?android:attr/textColorSecondary" />
</com.android.systemui.util.DualHeightHorizontalLinearLayout>
diff --git a/packages/SystemUI/res/layout/wallet_fullscreen.xml b/packages/SystemUI/res/layout/wallet_fullscreen.xml
index c4939c6..d365aa3 100644
--- a/packages/SystemUI/res/layout/wallet_fullscreen.xml
+++ b/packages/SystemUI/res/layout/wallet_fullscreen.xml
@@ -36,8 +36,8 @@
android:orientation="vertical">
<ImageView
android:id="@+id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="@dimen/wallet_screen_header_view_size"
+ android:layout_height="@dimen/wallet_screen_header_view_size"
android:layout_gravity="center_horizontal"
android:layout_marginVertical="10dp"
android:scaleType="center"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7e56063..fa4771d 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -623,8 +623,6 @@
<dimen name="qs_notif_collapsed_space">64dp</dimen>
- <dimen name="qs_container_bottom_padding">24dp</dimen>
-
<!-- Desired qs icon overlay size. -->
<dimen name="qs_detail_icon_overlay_size">24dp</dimen>
@@ -1492,7 +1490,8 @@
<!-- Wallet activity screen specs -->
<dimen name="wallet_icon_size">36sp</dimen>
- <dimen name="wallet_view_header_icon_size">56dp</dimen>
+ <dimen name="wallet_screen_header_icon_size">56dp</dimen>
+ <dimen name="wallet_screen_header_view_size">80dp</dimen>
<dimen name="card_margin">16dp</dimen>
<dimen name="card_carousel_dot_offset">24dp</dimen>
<dimen name="card_carousel_dot_unselected_radius">2dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 150fc73..4c9618f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2968,4 +2968,6 @@
<!-- Message shown to suggest authentication using [CHAR LIMIT=60]-->
<string name="keyguard_try_fingerprint">Use fingerprint to open</string>
+ <!-- Content description for a chip in the status bar showing that the user is currently on a phone call. [CHAR LIMIT=NONE] -->
+ <string name="ongoing_phone_call_content_description">Ongoing phone call</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 4b71a3a..baf3458 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -19,42 +19,22 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import android.app.PendingIntent;
import android.app.WallpaperManager;
-import android.app.smartspace.SmartspaceConfig;
-import android.app.smartspace.SmartspaceManager;
-import android.app.smartspace.SmartspaceSession;
-import android.app.smartspace.SmartspaceTarget;
-import android.content.Intent;
-import android.content.pm.UserInfo;
import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.BcSmartspaceDataPlugin;
-import com.android.systemui.plugins.BcSmartspaceDataPlugin.IntentStarter;
import com.android.systemui.plugins.ClockPlugin;
-import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -62,14 +42,10 @@
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.ViewController;
-import com.android.systemui.util.settings.SecureSettings;
import java.util.Locale;
-import java.util.Optional;
import java.util.TimeZone;
-import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -85,9 +61,8 @@
private final KeyguardSliceViewController mKeyguardSliceViewController;
private final NotificationIconAreaController mNotificationIconAreaController;
private final BroadcastDispatcher mBroadcastDispatcher;
- private final Executor mUiExecutor;
private final BatteryController mBatteryController;
- private final FeatureFlags mFeatureFlags;
+ private final LockscreenSmartspaceController mSmartspaceController;
/**
* Clock for both small and large sizes
@@ -97,20 +72,8 @@
private AnimatableClockController mLargeClockViewController;
private FrameLayout mLargeClockFrame;
- private SmartspaceSession mSmartspaceSession;
- private SmartspaceSession.OnTargetsAvailableListener mSmartspaceCallback;
- private ConfigurationController mConfigurationController;
- private ActivityStarter mActivityStarter;
- private FalsingManager mFalsingManager;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final KeyguardBypassController mBypassController;
- private Handler mHandler;
- private UserTracker mUserTracker;
- private SecureSettings mSecureSettings;
- private ContentObserver mSettingsObserver;
- private boolean mShowSensitiveContentForCurrentUser;
- private boolean mShowSensitiveContentForManagedUser;
- private UserHandle mManagedUserHandle;
/**
* Listener for changes to the color palette.
@@ -118,59 +81,30 @@
* The color palette changes when the wallpaper is changed.
*/
private final ColorExtractor.OnColorsChangedListener mColorsListener =
- new ColorExtractor.OnColorsChangedListener() {
- @Override
- public void onColorsChanged(ColorExtractor extractor, int which) {
- if ((which & WallpaperManager.FLAG_LOCK) != 0) {
- mView.updateColors(getGradientColors());
- }
- }
- };
-
- private final ConfigurationController.ConfigurationListener mConfigurationListener =
- new ConfigurationController.ConfigurationListener() {
- @Override
- public void onThemeChanged() {
- updateWallpaperColor();
- }
- };
-
- private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
-
- private final StatusBarStateController.StateListener mStatusBarStateListener =
- new StatusBarStateController.StateListener() {
- @Override
- public void onDozeAmountChanged(float linear, float eased) {
- if (mSmartspaceView != null) {
- mSmartspaceView.setDozeAmount(eased);
- }
+ (extractor, which) -> {
+ if ((which & WallpaperManager.FLAG_LOCK) != 0) {
+ mView.updateColors(getGradientColors());
}
};
+ private final ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
+
// If set, will replace keyguard_status_area
- private BcSmartspaceDataPlugin.SmartspaceView mSmartspaceView;
- private Optional<BcSmartspaceDataPlugin> mSmartspacePlugin;
+ private View mSmartspaceView;
@Inject
public KeyguardClockSwitchController(
KeyguardClockSwitch keyguardClockSwitch,
StatusBarStateController statusBarStateController,
- SysuiColorExtractor colorExtractor, ClockManager clockManager,
+ SysuiColorExtractor colorExtractor,
+ ClockManager clockManager,
KeyguardSliceViewController keyguardSliceViewController,
NotificationIconAreaController notificationIconAreaController,
BroadcastDispatcher broadcastDispatcher,
- FeatureFlags featureFlags,
- @Main Executor uiExecutor,
BatteryController batteryController,
- ConfigurationController configurationController,
- ActivityStarter activityStarter,
- FalsingManager falsingManager,
KeyguardUpdateMonitor keyguardUpdateMonitor,
KeyguardBypassController bypassController,
- @Main Handler handler,
- UserTracker userTracker,
- SecureSettings secureSettings,
- Optional<BcSmartspaceDataPlugin> smartspacePlugin) {
+ LockscreenSmartspaceController smartspaceController) {
super(keyguardClockSwitch);
mStatusBarStateController = statusBarStateController;
mColorExtractor = colorExtractor;
@@ -178,18 +112,10 @@
mKeyguardSliceViewController = keyguardSliceViewController;
mNotificationIconAreaController = notificationIconAreaController;
mBroadcastDispatcher = broadcastDispatcher;
- mFeatureFlags = featureFlags;
- mUiExecutor = uiExecutor;
mBatteryController = batteryController;
- mConfigurationController = configurationController;
- mActivityStarter = activityStarter;
- mFalsingManager = falsingManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mBypassController = bypassController;
- mHandler = handler;
- mUserTracker = userTracker;
- mSecureSettings = secureSettings;
- mSmartspacePlugin = smartspacePlugin;
+ mSmartspaceController = smartspaceController;
}
/**
@@ -232,119 +158,33 @@
mBypassController);
mLargeClockViewController.init();
- mStatusBarStateController.addCallback(mStatusBarStateListener);
- mConfigurationController.addCallback(mConfigurationListener);
+ if (mSmartspaceController.isEnabled()) {
+ mSmartspaceView = mSmartspaceController.buildAndConnectView(mView);
- if (mFeatureFlags.isSmartspaceEnabled() && mSmartspacePlugin.isPresent()) {
- BcSmartspaceDataPlugin smartspaceDataPlugin = mSmartspacePlugin.get();
View ksa = mView.findViewById(R.id.keyguard_status_area);
int ksaIndex = mView.indexOfChild(ksa);
ksa.setVisibility(View.GONE);
- mSmartspaceView = smartspaceDataPlugin.getView(mView);
- mSmartspaceView.registerDataProvider(smartspaceDataPlugin);
- mSmartspaceView.setIntentStarter(new IntentStarter() {
- public void startIntent(View v, Intent i) {
- mActivityStarter.startActivity(i, true /* dismissShade */);
- }
-
- public void startPendingIntent(PendingIntent pi) {
- mActivityStarter.startPendingIntentDismissingKeyguard(pi);
- }
- });
- mSmartspaceView.setFalsingManager(mFalsingManager);
- updateWallpaperColor();
- View asView = (View) mSmartspaceView;
-
// Place smartspace view below normal clock...
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
MATCH_PARENT, WRAP_CONTENT);
lp.addRule(RelativeLayout.BELOW, R.id.lockscreen_clock_view);
- mView.addView(asView, ksaIndex, lp);
+ mView.addView(mSmartspaceView, ksaIndex, lp);
int padding = getContext().getResources()
.getDimensionPixelSize(R.dimen.below_clock_padding_start);
- asView.setPadding(padding, 0, padding, 0);
+ mSmartspaceView.setPadding(padding, 0, padding, 0);
// ... but above the large clock
lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
- lp.addRule(RelativeLayout.BELOW, asView.getId());
+ lp.addRule(RelativeLayout.BELOW, mSmartspaceView.getId());
mLargeClockFrame.setLayoutParams(lp);
View nic = mView.findViewById(
R.id.left_aligned_notification_icon_container);
lp = (RelativeLayout.LayoutParams) nic.getLayoutParams();
- lp.addRule(RelativeLayout.BELOW, asView.getId());
+ lp.addRule(RelativeLayout.BELOW, mSmartspaceView.getId());
nic.setLayoutParams(lp);
-
- mSmartspaceSession = getContext().getSystemService(SmartspaceManager.class)
- .createSmartspaceSession(
- new SmartspaceConfig.Builder(getContext(), "lockscreen").build());
- mSmartspaceCallback = targets -> {
- targets.removeIf(this::filterSmartspaceTarget);
- smartspaceDataPlugin.onTargetsAvailable(targets);
- };
- mSmartspaceSession.addOnTargetsAvailableListener(mUiExecutor, mSmartspaceCallback);
- mSettingsObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- reloadSmartspace();
- }
- };
-
- getContext().getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
- true, mSettingsObserver, UserHandle.USER_ALL);
- reloadSmartspace();
- }
-
- float dozeAmount = mStatusBarStateController.getDozeAmount();
- mStatusBarStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
- }
-
- @VisibleForTesting
- boolean filterSmartspaceTarget(SmartspaceTarget t) {
- if (!t.isSensitive()) return false;
-
- if (t.getUserHandle().equals(mUserTracker.getUserHandle())) {
- return !mShowSensitiveContentForCurrentUser;
- }
- if (t.getUserHandle().equals(mManagedUserHandle)) {
- return !mShowSensitiveContentForManagedUser;
- }
-
- return false;
- }
-
- private void reloadSmartspace() {
- mManagedUserHandle = getWorkProfileUser();
- final String setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
-
- mShowSensitiveContentForCurrentUser =
- mSecureSettings.getIntForUser(setting, 0, mUserTracker.getUserId()) == 1;
- if (mManagedUserHandle != null) {
- int id = mManagedUserHandle.getIdentifier();
- mShowSensitiveContentForManagedUser =
- mSecureSettings.getIntForUser(setting, 0, id) == 1;
- }
-
- mSmartspaceSession.requestSmartspaceUpdate();
- }
-
- private UserHandle getWorkProfileUser() {
- for (UserInfo userInfo : mUserTracker.getUserProfiles()) {
- if (userInfo.isManagedProfile()) {
- return userInfo.getUserHandle();
- }
- }
- return null;
- }
-
- private void updateWallpaperColor() {
- if (mSmartspaceView != null) {
- int color = Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColor);
- mSmartspaceView.setPrimaryTextColor(color);
}
}
@@ -356,16 +196,16 @@
mColorExtractor.removeOnColorsChangedListener(mColorsListener);
mView.setClockPlugin(null, mStatusBarStateController.getState());
- if (mSmartspaceSession != null) {
- mSmartspaceSession.removeOnTargetsAvailableListener(mSmartspaceCallback);
- mSmartspaceSession.close();
- mSmartspaceSession = null;
- }
- mStatusBarStateController.removeCallback(mStatusBarStateListener);
- mConfigurationController.removeCallback(mConfigurationListener);
+ mSmartspaceController.disconnect();
- if (mSettingsObserver != null) {
- getContext().getContentResolver().unregisterContentObserver(mSettingsObserver);
+ // TODO: This is an unfortunate necessity since smartspace plugin retains a single instance
+ // of the smartspace view -- if we don't remove the view, it can't be reused by a later
+ // instance of this class. In order to fix this, we need to modify the plugin so that
+ // (a) we get a new view each time and (b) we can properly clean up an old view by making
+ // it unregister itself as a plugin listener.
+ if (mSmartspaceView != null) {
+ mView.removeView(mSmartspaceView);
+ mSmartspaceView = null;
}
}
@@ -436,7 +276,7 @@
scale, props, animate);
if (mSmartspaceView != null) {
- PropertyAnimator.setProperty((View) mSmartspaceView, AnimatableProperty.TRANSLATION_X,
+ PropertyAnimator.setProperty(mSmartspaceView, AnimatableProperty.TRANSLATION_X,
x, props, animate);
}
@@ -510,14 +350,4 @@
private int getCurrentLayoutDirection() {
return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault());
}
-
- @VisibleForTesting
- ConfigurationController.ConfigurationListener getConfigurationListener() {
- return mConfigurationListener;
- }
-
- @VisibleForTesting
- ContentObserver getSettingsObserver() {
- return mSettingsObserver;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index be50eb1..afda2a4 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -55,8 +55,6 @@
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.os.HandlerThread;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings.Secure;
@@ -91,11 +89,13 @@
import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.ThreadFactory;
import com.android.systemui.util.settings.SecureSettings;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -122,7 +122,7 @@
@VisibleForTesting
protected boolean mIsRegistered;
private final BroadcastDispatcher mBroadcastDispatcher;
- private final Handler mMainHandler;
+ private final Executor mMainExecutor;
private final TunerService mTunerService;
private final SecureSettings mSecureSettings;
private DisplayManager.DisplayListener mDisplayListener;
@@ -153,6 +153,7 @@
private WindowManager mWindowManager;
private int mRotation;
private SecureSetting mColorInversionSetting;
+ private DelayableExecutor mExecutor;
private Handler mHandler;
private boolean mPendingRotationChange;
private boolean mIsRoundedCornerMultipleRadius;
@@ -212,7 +213,7 @@
@Inject
public ScreenDecorations(Context context,
- @Main Handler handler,
+ @Main Executor mainExecutor,
SecureSettings secureSettings,
BroadcastDispatcher broadcastDispatcher,
TunerService tunerService,
@@ -220,7 +221,7 @@
PrivacyDotViewController dotViewController,
ThreadFactory threadFactory) {
super(context);
- mMainHandler = handler;
+ mMainExecutor = mainExecutor;
mSecureSettings = secureSettings;
mBroadcastDispatcher = broadcastDispatcher;
mTunerService = tunerService;
@@ -235,17 +236,10 @@
Log.i(TAG, "ScreenDecorations is disabled");
return;
}
- mHandler = startHandlerThread();
- mHandler.post(this::startOnScreenDecorationsThread);
- mDotViewController.setUiExecutor(
- mThreadFactory.buildDelayableExecutorOnLooper(mHandler.getLooper()));
- }
-
- @VisibleForTesting
- Handler startHandlerThread() {
- HandlerThread thread = new HandlerThread("ScreenDecorations");
- thread.start();
- return thread.getThreadHandler();
+ mHandler = mThreadFactory.builderHandlerOnNewThread("ScreenDecorations");
+ mExecutor = mThreadFactory.buildDelayableExecutorOnHandler(mHandler);
+ mExecutor.execute(this::startOnScreenDecorationsThread);
+ mDotViewController.setUiExecutor(mExecutor);
}
private void startOnScreenDecorationsThread() {
@@ -332,7 +326,7 @@
mDisplayManager.getDisplay(DEFAULT_DISPLAY).getMetrics(metrics);
mDensity = metrics.density;
- mMainHandler.post(() -> mTunerService.addTunable(this, SIZE));
+ mExecutor.execute(() -> mTunerService.addTunable(this, SIZE));
// Watch color inversion and invert the overlay as needed.
if (mColorInversionSetting == null) {
@@ -351,10 +345,10 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
mBroadcastDispatcher.registerReceiver(mUserSwitchIntentReceiver, filter,
- new HandlerExecutor(mHandler), UserHandle.ALL);
+ mExecutor, UserHandle.ALL);
mIsRegistered = true;
} else {
- mMainHandler.post(() -> mTunerService.removeTunable(this));
+ mMainExecutor.execute(() -> mTunerService.removeTunable(this));
if (mColorInversionSetting != null) {
mColorInversionSetting.setListening(false);
@@ -567,7 +561,7 @@
Resources res = mContext.getResources();
boolean enabled = res.getBoolean(R.bool.config_enableDisplayCutoutProtection);
if (enabled) {
- mCameraListener = CameraAvailabilityListener.Factory.build(mContext, mHandler::post);
+ mCameraListener = CameraAvailabilityListener.Factory.build(mContext, mExecutor);
mCameraListener.addTransitionCallback(mCameraTransitionCallback);
mCameraListener.startListening();
}
@@ -624,7 +618,7 @@
Log.i(TAG, "ScreenDecorations is disabled");
return;
}
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
int oldRotation = mRotation;
mPendingRotationChange = false;
updateOrientation();
@@ -825,7 +819,7 @@
Log.i(TAG, "ScreenDecorations is disabled");
return;
}
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
if (mOverlays == null) return;
if (SIZE.equals(key)) {
Point size = mRoundedDefault;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index cd5abd7..2c48d0a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -36,7 +36,7 @@
public class UdfpsEnrollDrawable extends UdfpsDrawable {
private static final String TAG = "UdfpsAnimationEnroll";
- static final float PROGRESS_BAR_RADIUS = 180.f;
+ static final float PROGRESS_BAR_RADIUS = 360.f;
@NonNull private final Drawable mMovingTargetFpIcon;
@NonNull private final Paint mSensorOutlinePaint;
@@ -96,11 +96,6 @@
return;
}
- if (mSensorRect != null) {
- canvas.drawOval(mSensorRect, mSensorOutlinePaint);
- }
- mFingerprintDrawable.draw(canvas);
-
// Draw moving target
if (mEnrollHelper.isCenterEnrollmentComplete()) {
mFingerprintDrawable.setAlpha(mAlpha == 255 ? 64 : mAlpha);
@@ -117,6 +112,10 @@
mMovingTargetFpIcon.draw(canvas);
canvas.restore();
} else {
+ if (mSensorRect != null) {
+ canvas.drawOval(mSensorRect, mSensorOutlinePaint);
+ }
+ mFingerprintDrawable.draw(canvas);
mFingerprintDrawable.setAlpha(mAlpha);
mSensorOutlinePaint.setAlpha(mAlpha);
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index a904cef..26be987 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -120,7 +120,7 @@
private val onSeedingComplete = Consumer<Boolean> {
accepted ->
if (accepted) {
- selectedStructure = controlsController.get().getFavorites().maxByOrNull {
+ selectedStructure = controlsController.get().getFavorites().maxBy {
it.controls.size
} ?: EMPTY_STRUCTURE
updatePreferences(selectedStructure)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index fd80d50..26db33d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -29,6 +29,7 @@
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
import android.app.role.RoleManager;
+import android.app.smartspace.SmartspaceManager;
import android.app.trust.TrustManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -400,4 +401,10 @@
static PermissionManager providePermissionManager(Context context) {
return context.getSystemService(PermissionManager.class);
}
+
+ @Provides
+ @Singleton
+ static SmartspaceManager provideSmartspaceManager(Context context) {
+ return context.getSystemService(SmartspaceManager.class);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 816cf61..ef53233 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -535,12 +535,20 @@
this.desiredLocation = desiredLocation
this.desiredHostState = it
currentlyExpanded = it.expansion > 0
+
+ val shouldCloseGuts = !currentlyExpanded && !mediaManager.hasActiveMedia() &&
+ desiredHostState.showsOnlyActiveMedia
+
for (mediaPlayer in MediaPlayerData.players()) {
if (animate) {
mediaPlayer.mediaViewController.animatePendingStateChange(
duration = duration,
delay = startDelay)
}
+ if (shouldCloseGuts && mediaPlayer.mediaViewController.isGutsVisible) {
+ mediaPlayer.closeGuts(!animate)
+ }
+
mediaPlayer.mediaViewController.onLocationPreChange(desiredLocation)
}
mediaCarouselScrollHandler.showsSettingsButton = !it.showsOnlyActiveMedia
@@ -556,9 +564,9 @@
}
}
- fun closeGuts() {
+ fun closeGuts(immediate: Boolean = true) {
MediaPlayerData.players().forEach {
- it.closeGuts(true)
+ it.closeGuts(immediate)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
index e5a6271..c806bcf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
@@ -59,7 +59,7 @@
private val mainExecutor: DelayableExecutor,
private val dismissCallback: () -> Unit,
private var translationChangedListener: () -> Unit,
- private val closeGuts: () -> Unit,
+ private val closeGuts: (immediate: Boolean) -> Unit,
private val falsingCollector: FalsingCollector,
private val falsingManager: FalsingManager,
private val logSmartspaceImpression: () -> Unit
@@ -473,7 +473,7 @@
if (oldIndex != visibleMediaIndex && visibleToUser) {
logSmartspaceImpression()
}
- closeGuts()
+ closeGuts(false)
updatePlayerVisibilities()
}
val relativeLocation = visibleMediaIndex.toFloat() + if (playerWidthPlusPadding > 0)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index bc11fee..3e9559b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -213,7 +213,8 @@
mMediaViewController.openGuts();
return true;
} else {
- return false;
+ closeGuts();
+ return true;
}
});
mPlayerViewHolder.getCancel().setOnClickListener(v -> {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 3c28f6e..60e832a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -273,6 +273,7 @@
} else {
updateDesiredLocation()
qsExpanded = false
+ closeGuts()
}
mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 29685a4..7b5ab0d 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -416,9 +416,8 @@
&& birthdayString == null;
boolean addBirthdayStatus = !hasBirthdayStatus(storedTile, context)
&& birthdayString != null;
- boolean shouldUpdate =
- storedTile.getContactAffinity() != affinity || outdatedBirthdayStatus
- || addBirthdayStatus;
+ boolean shouldUpdate = storedTile.getContactAffinity() != affinity || outdatedBirthdayStatus
+ || addBirthdayStatus;
if (shouldUpdate) {
if (DEBUG) Log.d(TAG, "Update " + storedTile.getUserName() + " from contacts");
manager.updateAppWidgetOptionsAndView(appWidgetId,
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 06f8a60..9fc9cad 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -41,8 +41,6 @@
import android.app.people.PeopleSpaceTile;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
@@ -57,13 +55,13 @@
import android.util.IconDrawableFactory;
import android.util.Log;
import android.util.Pair;
+import android.view.Gravity;
import android.view.View;
import android.widget.RemoteViews;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.launcher3.icons.FastBitmapDrawable;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
@@ -195,16 +193,10 @@
*/
private RemoteViews getViewForTile() {
if (DEBUG) Log.d(TAG, "Creating view for tile key: " + mKey.toString());
- if (mTile == null || mTile.isPackageSuspended() || mTile.isUserQuieted()) {
- if (DEBUG) Log.d(TAG, "Create empty view: " + mTile);
- return createEmptyView();
- }
-
- boolean dndBlockingTileData = isDndBlockingTileData(mTile);
- if (dndBlockingTileData) {
- if (DEBUG) Log.d(TAG, "Create DND view: " + mTile.getNotificationPolicyState());
- // TODO: Create DND view.
- return createEmptyView();
+ if (mTile == null || mTile.isPackageSuspended() || mTile.isUserQuieted()
+ || isDndBlockingTileData(mTile)) {
+ if (DEBUG) Log.d(TAG, "Create suppressed view: " + mTile);
+ return createSuppressedView();
}
if (Objects.equals(mTile.getNotificationCategory(), CATEGORY_MISSED_CALL)) {
@@ -265,34 +257,27 @@
return !tile.canBypassDnd();
}
- private RemoteViews createEmptyView() {
- RemoteViews views = new RemoteViews(mContext.getPackageName(),
- R.layout.people_tile_empty_layout);
- Drawable appIcon = getAppBadge(mKey.getPackageName(), mKey.getUserId());
+ private RemoteViews createSuppressedView() {
+ RemoteViews views;
+ if (mTile.isUserQuieted()) {
+ views = new RemoteViews(mContext.getPackageName(),
+ R.layout.people_tile_work_profile_quiet_layout);
+ } else {
+ views = new RemoteViews(mContext.getPackageName(),
+ R.layout.people_tile_suppressed_layout);
+ }
+ Drawable appIcon = mContext.getDrawable(R.drawable.ic_conversation_icon);
Bitmap appIconAsBitmap = convertDrawableToBitmap(appIcon);
- FastBitmapDrawable drawable = new FastBitmapDrawable(
- appIconAsBitmap);
+ FastBitmapDrawable drawable = new FastBitmapDrawable(appIconAsBitmap);
drawable.setIsDisabled(true);
Bitmap convertedBitmap = convertDrawableToBitmap(drawable);
- views.setImageViewBitmap(R.id.item, convertedBitmap);
+ views.setImageViewBitmap(R.id.icon, convertedBitmap);
return views;
}
- private Drawable getAppBadge(String packageName, int userId) {
- Drawable badge = null;
- try {
- final ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfoAsUser(
- packageName, PackageManager.GET_META_DATA, userId);
- badge = Utils.getBadgedIcon(mContext, appInfo);
- } catch (PackageManager.NameNotFoundException e) {
- badge = mContext.getPackageManager().getDefaultActivityIcon();
- }
- return badge;
- }
-
private void setMaxLines(RemoteViews views, boolean showSender) {
int textSize = mLayoutSize == LAYOUT_LARGE ? getSizeInDp(
- R.dimen.content_text_size_for_medium)
+ R.dimen.content_text_size_for_large)
: getSizeInDp(R.dimen.content_text_size_for_medium);
int lineHeight = getLineHeight(textSize);
int notificationContentHeight = getContentHeightForLayout(lineHeight);
@@ -422,9 +407,6 @@
views.setViewVisibility(R.id.availability, View.GONE);
}
- if (mTile.getUserName() != null) {
- views.setTextViewText(R.id.name, mTile.getUserName().toString());
- }
views.setBoolean(R.id.image, "setClipToOutline", true);
views.setImageViewBitmap(R.id.person_icon,
getPersonIconBitmap(mContext, mTile, maxAvatarSize));
@@ -537,25 +519,31 @@
statusText = getStatusTextByType(status.getActivity());
}
views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
- views.setViewVisibility(R.id.messages_count, View.GONE);
- setMaxLines(views, false);
- // Secondary text color for statuses.
- views.setColorAttr(R.id.text_content, "setTextColor", android.R.attr.textColorSecondary);
views.setTextViewText(R.id.text_content, statusText);
+ if (mLayoutSize == LAYOUT_LARGE) {
+ views.setInt(R.id.content, "setGravity", Gravity.BOTTOM);
+ }
Icon statusIcon = status.getIcon();
if (statusIcon != null) {
- // No multi-line text with status images on medium layout.
- views.setViewVisibility(R.id.text_content, View.GONE);
+ // No text content styled text on medium or large.
+ views.setViewVisibility(R.id.scrim_layout, View.VISIBLE);
+ views.setImageViewIcon(R.id.status_icon, statusIcon);
// Show 1-line subtext on large layout with status images.
if (mLayoutSize == LAYOUT_LARGE) {
- views.setViewVisibility(R.id.subtext, View.VISIBLE);
- views.setTextViewText(R.id.subtext, statusText);
+ if (DEBUG) Log.d(TAG, "Remove name for large");
+ views.setViewVisibility(R.id.name, View.GONE);
+ views.setColorAttr(R.id.text_content, "setTextColor",
+ android.R.attr.textColorPrimary);
+ } else if (mLayoutSize == LAYOUT_MEDIUM) {
+ views.setViewVisibility(R.id.text_content, View.GONE);
+ views.setTextViewText(R.id.name, statusText);
}
- views.setViewVisibility(R.id.image, View.VISIBLE);
- views.setImageViewIcon(R.id.image, statusIcon);
} else {
- views.setViewVisibility(R.id.image, View.GONE);
+ // Secondary text color for statuses without icons.
+ views.setColorAttr(R.id.text_content, "setTextColor",
+ android.R.attr.textColorSecondary);
+ setMaxLines(views, false);
}
// TODO: Set status pre-defined icons
views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_person);
@@ -741,16 +729,23 @@
views.setViewVisibility(R.id.name, View.VISIBLE);
views.setViewVisibility(R.id.text_content, View.VISIBLE);
views.setViewVisibility(R.id.subtext, View.GONE);
+ views.setViewVisibility(R.id.image, View.GONE);
+ views.setViewVisibility(R.id.scrim_layout, View.GONE);
}
if (mLayoutSize == LAYOUT_MEDIUM) {
if (DEBUG) Log.d(TAG, "Set vertical padding: " + mMediumVerticalPadding);
int horizontalPadding = (int) Math.floor(MAX_MEDIUM_PADDING * mDensity);
int verticalPadding = (int) Math.floor(mMediumVerticalPadding * mDensity);
- views.setViewPadding(R.id.item, horizontalPadding, verticalPadding, horizontalPadding,
+ views.setViewPadding(R.id.content, horizontalPadding, verticalPadding,
+ horizontalPadding,
verticalPadding);
}
views.setViewVisibility(R.id.messages_count, View.GONE);
+ if (mTile.getUserName() != null) {
+ views.setTextViewText(R.id.name, mTile.getUserName());
+ }
+
return views;
}
@@ -761,6 +756,9 @@
views.setViewVisibility(R.id.predefined_icon, View.GONE);
views.setViewVisibility(R.id.messages_count, View.GONE);
}
+ if (mTile.getUserName() != null) {
+ views.setTextViewText(R.id.name, mTile.getUserName());
+ }
String status = getLastInteractionString(mContext,
mTile.getLastInteractionTimestamp());
if (status != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
index eec69f98..1d2e747 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
@@ -28,7 +28,7 @@
appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
.toList()
.sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
- { it.second.minOrNull() })) // Sort by "smallest" AppOpp (Location is largest)
+ { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest)
types = itemsList.map { it.privacyType }.distinct().sorted()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index c459963..3a3f3f1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -129,6 +129,12 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mNavBarInset = insets.getInsets(WindowInsets.Type.navigationBars()).bottom;
+ mQSPanelContainer.setPaddingRelative(
+ mQSPanelContainer.getPaddingStart(),
+ mQSPanelContainer.getPaddingTop(),
+ mQSPanelContainer.getPaddingEnd(),
+ mNavBarInset
+ );
return super.onApplyWindowInsets(insets);
}
@@ -138,8 +144,7 @@
// bottom and footer are inside the screen.
MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanelContainer.getLayoutParams();
- int availableScreenHeight = getDisplayHeight() - mNavBarInset;
- int maxQs = availableScreenHeight - layoutParams.topMargin - layoutParams.bottomMargin
+ int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin
- getPaddingBottom();
int padding = mPaddingLeft + mPaddingRight + layoutParams.leftMargin
+ layoutParams.rightMargin;
@@ -148,10 +153,8 @@
mQSPanelContainer.measure(qsPanelWidthSpec,
MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.AT_MOST));
int width = mQSPanelContainer.getMeasuredWidth() + padding;
- int height = layoutParams.topMargin + layoutParams.bottomMargin
- + mQSPanelContainer.getMeasuredHeight() + getPaddingBottom();
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(availableScreenHeight, MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(getDisplayHeight(), MeasureSpec.EXACTLY));
// QSCustomizer will always be the height of the screen, but do this after
// other measuring to avoid changing the height of the QS.
mQSCustomizer.measure(widthMeasureSpec,
@@ -196,13 +199,10 @@
void updateResources(QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController) {
- mQSPanelContainer.setPaddingRelative(
- mQSPanelContainer.getPaddingStart(),
- mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.quick_qs_offset_height),
- mQSPanelContainer.getPaddingEnd(),
- mContext.getResources().getDimensionPixelSize(R.dimen.qs_container_bottom_padding)
- );
+ LayoutParams layoutParams = (LayoutParams) mQSPanelContainer.getLayoutParams();
+ layoutParams.topMargin = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.quick_qs_offset_height);
+ mQSPanelContainer.setLayoutParams(layoutParams);
int sideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
int padding = getResources().getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 05197e4..0335319 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -30,6 +30,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
+import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -153,11 +154,18 @@
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
lp.topMargin = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
- lp.bottomMargin = mContext.getResources().getDimensionPixelSize(
- R.dimen.qs_container_bottom_padding);
setLayoutParams(lp);
}
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ int bottomNavBar = insets.getInsets(WindowInsets.Type.navigationBars()).bottom;
+ MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
+ lp.bottomMargin = bottomNavBar;
+ setLayoutParams(lp);
+ return super.onApplyWindowInsets(insets);
+ }
+
public boolean isClosingDetail() {
return mClosingDetail;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 26332f4..81b5318 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -23,6 +23,7 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.FeatureFlagUtils;
import android.util.Pair;
import android.view.DisplayCutout;
import android.view.View;
@@ -87,13 +88,21 @@
private int mTopViewMeasureHeight;
private final String mMobileSlotName;
+ private final String mNoCallingSlotName;
private final String mCallStrengthSlotName;
+ private final boolean mProviderModel;
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
- mMobileSlotName = context.getString(com.android.internal.R.string.status_bar_no_calling);
+ mMobileSlotName = context.getString(com.android.internal.R.string.status_bar_mobile);
+ mNoCallingSlotName = context.getString(com.android.internal.R.string.status_bar_no_calling);
mCallStrengthSlotName =
context.getString(com.android.internal.R.string.status_bar_call_strength);
+ if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+ mProviderModel = true;
+ } else {
+ mProviderModel = false;
+ }
}
/**
@@ -242,14 +251,24 @@
.setListener(new TouchAnimator.ListenerAdapter() {
@Override
public void onAnimationAtEnd() {
- mIconContainer.addIgnoredSlot(mMobileSlotName);
- mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
+ // TODO(b/185580157): Remove the mProviderModel if the mobile slot can be
+ // hidden in Provider model.
+ if (mProviderModel) {
+ mIconContainer.addIgnoredSlot(mNoCallingSlotName);
+ mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
+ } else {
+ mIconContainer.addIgnoredSlot(mMobileSlotName);
+ }
}
@Override
public void onAnimationStarted() {
- mIconContainer.addIgnoredSlot(mMobileSlotName);
- mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
+ if (mProviderModel) {
+ mIconContainer.addIgnoredSlot(mNoCallingSlotName);
+ mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
+ } else {
+ mIconContainer.addIgnoredSlot(mMobileSlotName);
+ }
setSeparatorVisibility(false);
}
@@ -257,8 +276,12 @@
@Override
public void onAnimationAtStart() {
super.onAnimationAtStart();
- mIconContainer.removeIgnoredSlot(mMobileSlotName);
- mIconContainer.removeIgnoredSlot(mCallStrengthSlotName);
+ if (mProviderModel) {
+ mIconContainer.removeIgnoredSlot(mNoCallingSlotName);
+ mIconContainer.removeIgnoredSlot(mCallStrengthSlotName);
+ } else {
+ mIconContainer.removeIgnoredSlot(mMobileSlotName);
+ }
setSeparatorVisibility(mShowClockIconsSeparator);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt
index 7055760..2bac298 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt
@@ -45,13 +45,15 @@
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
if (ignoreLastView && childCount > 0) {
val lastView = getChildAt(childCount - 1)
- val lp = lastView.layoutParams as MarginLayoutParams
- if (orientation == VERTICAL) {
- val height = lastView.measuredHeight + lp.bottomMargin + lp.topMargin
- setMeasuredDimension(measuredWidth, measuredHeight - height)
- } else {
- val width = lastView.measuredWidth + lp.leftMargin + lp.rightMargin
- setMeasuredDimension(measuredWidth - width, measuredHeight)
+ if (lastView.visibility != GONE) {
+ val lp = lastView.layoutParams as MarginLayoutParams
+ if (orientation == VERTICAL) {
+ val height = lastView.measuredHeight + lp.bottomMargin + lp.topMargin
+ setMeasuredDimension(measuredWidth, measuredHeight - height)
+ } else {
+ val width = lastView.measuredWidth + lp.leftMargin + lp.rightMargin
+ setMeasuredDimension(measuredWidth - width, measuredHeight)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
index 1c5df41..5b69497 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
@@ -430,7 +430,6 @@
private static class LeftIconApplicator implements ResultApplicator {
public static final int[] MARGIN_ADJUSTED_VIEWS = {
- R.id.notification_headerless_view_column,
R.id.text,
R.id.big_text,
R.id.title,
@@ -438,22 +437,31 @@
R.id.notification_header};
@Override
- public void apply(View parent, View child, boolean apply, boolean reset) {
- ImageView rightIcon = child.findViewById(com.android.internal.R.id.right_icon);
+ public void apply(View parent, View child, boolean showLeftIcon, boolean reset) {
ImageView leftIcon = child.findViewById(com.android.internal.R.id.left_icon);
- if (rightIcon == null || leftIcon == null) {
+ if (leftIcon == null) {
return;
}
- Drawable iconDrawable = rightIcon.getDrawable();
- if (iconDrawable == null) {
- return;
+ ImageView rightIcon = child.findViewById(com.android.internal.R.id.right_icon);
+ boolean keepRightIcon = rightIcon != null && Integer.valueOf(1).equals(
+ rightIcon.getTag(R.id.tag_keep_when_showing_left_icon));
+ boolean leftIconUsesRightIconDrawable = Integer.valueOf(1).equals(
+ leftIcon.getTag(R.id.tag_uses_right_icon_drawable));
+ if (leftIconUsesRightIconDrawable) {
+ // Use the right drawable when showing the left, unless the right is being kept
+ Drawable rightDrawable = rightIcon == null ? null : rightIcon.getDrawable();
+ leftIcon.setImageDrawable(showLeftIcon && !keepRightIcon ? rightDrawable : null);
}
- rightIcon.setVisibility(apply ? View.GONE : View.VISIBLE);
- leftIcon.setVisibility(apply ? View.VISIBLE : View.GONE);
- leftIcon.setImageDrawable(apply ? iconDrawable : null);
+ leftIcon.setVisibility(showLeftIcon ? View.VISIBLE : View.GONE);
- for (int viewId : MARGIN_ADJUSTED_VIEWS) {
- adjustMargins(!apply, child.findViewById(viewId));
+ // update the right icon as well
+ if (rightIcon != null) {
+ boolean showRightIcon = (keepRightIcon || !showLeftIcon)
+ && rightIcon.getDrawable() != null;
+ rightIcon.setVisibility(showRightIcon ? View.VISIBLE : View.GONE);
+ for (int viewId : MARGIN_ADJUSTED_VIEWS) {
+ adjustMargins(showRightIcon, child.findViewById(viewId));
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index eb7854e..4919593 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -61,6 +61,7 @@
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.util.DeviceConfigProxy;
@@ -243,11 +244,12 @@
SystemClock systemClock,
ActivityStarter activityStarter,
@Main Executor mainExecutor,
- IActivityManager iActivityManager) {
+ IActivityManager iActivityManager,
+ OngoingCallLogger logger) {
OngoingCallController ongoingCallController =
new OngoingCallController(
notifCollection, featureFlags, systemClock, activityStarter, mainExecutor,
- iActivityManager);
+ iActivityManager, logger);
ongoingCallController.init();
return ongoingCallController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
new file mode 100644
index 0000000..ce60c85
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.lockscreen
+
+import android.app.PendingIntent
+import android.app.smartspace.SmartspaceConfig
+import android.app.smartspace.SmartspaceManager
+import android.app.smartspace.SmartspaceSession
+import android.app.smartspace.SmartspaceTarget
+import android.content.ContentResolver
+import android.content.Context
+import android.content.Intent
+import android.content.pm.UserInfo
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
+import android.view.View
+import android.view.ViewGroup
+import com.android.settingslib.Utils
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.concurrency.Execution
+import com.android.systemui.util.settings.SecureSettings
+import java.lang.RuntimeException
+import java.util.Optional
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/**
+ * Controller for managing the smartspace view on the lockscreen
+ */
+@SysUISingleton
+class LockscreenSmartspaceController @Inject constructor(
+ private val context: Context,
+ private val featureFlags: FeatureFlags,
+ private val smartspaceManager: SmartspaceManager,
+ private val activityStarter: ActivityStarter,
+ private val falsingManager: FalsingManager,
+ private val secureSettings: SecureSettings,
+ private val userTracker: UserTracker,
+ private val contentResolver: ContentResolver,
+ private val configurationController: ConfigurationController,
+ private val statusBarStateController: StatusBarStateController,
+ private val execution: Execution,
+ @Main private val uiExecutor: Executor,
+ @Main private val handler: Handler,
+ optionalPlugin: Optional<BcSmartspaceDataPlugin>
+) {
+ private var session: SmartspaceSession? = null
+ private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
+ private lateinit var smartspaceView: SmartspaceView
+
+ lateinit var view: View
+ private set
+
+ private var showSensitiveContentForCurrentUser = false
+ private var showSensitiveContentForManagedUser = false
+ private var managedUserHandle: UserHandle? = null
+
+ fun isEnabled(): Boolean {
+ execution.assertIsMainThread()
+
+ return featureFlags.isSmartspaceEnabled && plugin != null
+ }
+
+ /**
+ * Constructs the smartspace view and connects it to the smartspace service. Subsequent calls
+ * are idempotent until [disconnect] is called.
+ */
+ fun buildAndConnectView(parent: ViewGroup): View {
+ execution.assertIsMainThread()
+
+ if (!isEnabled()) {
+ throw RuntimeException("Cannot build view when not enabled")
+ }
+
+ buildView(parent)
+ connectSession()
+
+ return view
+ }
+
+ private fun buildView(parent: ViewGroup) {
+ if (plugin == null || this::view.isInitialized) {
+ return
+ }
+
+ val ssView = plugin.getView(parent)
+ ssView.registerDataProvider(plugin)
+ ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter {
+ override fun startIntent(v: View?, i: Intent?) {
+ activityStarter.startActivity(i, true /* dismissShade */)
+ }
+
+ override fun startPendingIntent(pi: PendingIntent?) {
+ activityStarter.startPendingIntentDismissingKeyguard(pi)
+ }
+ })
+ ssView.setFalsingManager(falsingManager)
+
+ this.smartspaceView = ssView
+ this.view = ssView as View
+
+ updateTextColorFromWallpaper()
+ statusBarStateListener.onDozeAmountChanged(0f, statusBarStateController.dozeAmount)
+ }
+
+ private fun connectSession() {
+ if (plugin == null || session != null) {
+ return
+ }
+ val session = smartspaceManager.createSmartspaceSession(
+ SmartspaceConfig.Builder(context, "lockscreen").build())
+ session.addOnTargetsAvailableListener(uiExecutor, sessionListener)
+
+ userTracker.addCallback(userTrackerCallback, uiExecutor)
+ contentResolver.registerContentObserver(
+ secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+ true,
+ settingsObserver,
+ UserHandle.USER_ALL
+ )
+ configurationController.addCallback(configChangeListener)
+ statusBarStateController.addCallback(statusBarStateListener)
+
+ this.session = session
+
+ reloadSmartspace()
+ }
+
+ /**
+ * Disconnects the smartspace view from the smartspace service and cleans up any resources.
+ * Calling [buildAndConnectView] again will cause the same view to be reconnected to the
+ * service.
+ */
+ fun disconnect() {
+ execution.assertIsMainThread()
+
+ if (session == null) {
+ return
+ }
+
+ session?.let {
+ it.removeOnTargetsAvailableListener(sessionListener)
+ it.close()
+ }
+ userTracker.removeCallback(userTrackerCallback)
+ contentResolver.unregisterContentObserver(settingsObserver)
+ configurationController.removeCallback(configChangeListener)
+ statusBarStateController.removeCallback(statusBarStateListener)
+ session = null
+
+ plugin?.onTargetsAvailable(emptyList())
+ }
+
+ fun addListener(listener: SmartspaceTargetListener) {
+ execution.assertIsMainThread()
+ plugin?.registerListener(listener)
+ }
+
+ fun removeListener(listener: SmartspaceTargetListener) {
+ execution.assertIsMainThread()
+ plugin?.unregisterListener(listener)
+ }
+
+ private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
+ execution.assertIsMainThread()
+ val filteredTargets = targets.filter(::filterSmartspaceTarget)
+ plugin?.onTargetsAvailable(filteredTargets)
+ }
+
+ private val userTrackerCallback = object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ execution.assertIsMainThread()
+ reloadSmartspace()
+ }
+
+ override fun onProfilesChanged(profiles: List<UserInfo>) {
+ }
+ }
+
+ private val settingsObserver = object : ContentObserver(handler) {
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ execution.assertIsMainThread()
+ reloadSmartspace()
+ }
+ }
+
+ private val configChangeListener = object : ConfigurationController.ConfigurationListener {
+ override fun onThemeChanged() {
+ execution.assertIsMainThread()
+ updateTextColorFromWallpaper()
+ }
+ }
+
+ private val statusBarStateListener = object : StatusBarStateController.StateListener {
+ override fun onDozeAmountChanged(linear: Float, eased: Float) {
+ execution.assertIsMainThread()
+ smartspaceView.setDozeAmount(eased)
+ }
+ }
+
+ private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
+ return when (t.userHandle) {
+ userTracker.userHandle -> {
+ !t.isSensitive || showSensitiveContentForCurrentUser
+ }
+ managedUserHandle -> {
+ // Really, this should be "if this managed profile is associated with the current
+ // active user", but we don't have a good way to check that, so instead we cheat:
+ // Only the primary user can have an associated managed profile, so only show
+ // content for the managed profile if the primary user is active
+ userTracker.userHandle.identifier == UserHandle.USER_SYSTEM &&
+ (!t.isSensitive || showSensitiveContentForManagedUser)
+ }
+ else -> {
+ false
+ }
+ }
+ }
+
+ private fun updateTextColorFromWallpaper() {
+ val wallpaperTextColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor)
+ smartspaceView.setPrimaryTextColor(wallpaperTextColor)
+ }
+
+ private fun reloadSmartspace() {
+ val setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
+
+ showSensitiveContentForCurrentUser =
+ secureSettings.getIntForUser(setting, 0, userTracker.userId) == 1
+
+ managedUserHandle = getWorkProfileUser()
+ val managedId = managedUserHandle?.identifier
+ if (managedId != null) {
+ showSensitiveContentForManagedUser =
+ secureSettings.getIntForUser(setting, 0, managedId) == 1
+ }
+
+ session?.requestSmartspaceUpdate()
+ }
+
+ private fun getWorkProfileUser(): UserHandle? {
+ for (userInfo in userTracker.userProfiles) {
+ if (userInfo.isManagedProfile) {
+ return userInfo.userHandle
+ }
+ }
+ return null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
index 151840a..79f99b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -42,12 +42,17 @@
updateImageTag(row.getEntry().getSbn());
}
- private void updateImageTag(StatusBarNotification notification) {
- final Bundle extras = notification.getNotification().extras;
- Icon overriddenIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON_BIG);
- if (overriddenIcon != null) {
- mRightIcon.setTag(ImageTransformState.ICON_TAG, overriddenIcon);
- mLeftIcon.setTag(ImageTransformState.ICON_TAG, overriddenIcon);
+ private void updateImageTag(StatusBarNotification sbn) {
+ final Bundle extras = sbn.getNotification().extras;
+ boolean bigLargeIconSet = extras.containsKey(Notification.EXTRA_LARGE_ICON_BIG);
+ if (bigLargeIconSet) {
+ Icon bigLargeIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON_BIG);
+ mRightIcon.setTag(ImageTransformState.ICON_TAG, bigLargeIcon);
+ mLeftIcon.setTag(ImageTransformState.ICON_TAG, bigLargeIcon);
+ } else {
+ // Overwrite in case the superclass populated this tag with the promoted picture,
+ // which won't be right since this is the expanded state.
+ mRightIcon.setTag(ImageTransformState.ICON_TAG, getLargeIcon(sbn.getNotification()));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index 0548611..a4f1172 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -20,10 +20,12 @@
import static com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.DEFAULT_HEADER_VISIBLE_AMOUNT;
+import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
+import android.graphics.drawable.Icon;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.view.View;
@@ -32,6 +34,8 @@
import android.widget.ProgressBar;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.internal.util.ContrastColorUtil;
import com.android.internal.widget.NotificationActionListLayout;
import com.android.systemui.Dependency;
@@ -143,16 +147,14 @@
com.android.internal.R.dimen.notification_content_margin_top);
}
- private void resolveTemplateViews(StatusBarNotification notification) {
+ private void resolveTemplateViews(StatusBarNotification sbn) {
mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon);
if (mRightIcon != null) {
- mRightIcon.setTag(ImageTransformState.ICON_TAG,
- notification.getNotification().getLargeIcon());
+ mRightIcon.setTag(ImageTransformState.ICON_TAG, getRightIcon(sbn.getNotification()));
}
mLeftIcon = mView.findViewById(com.android.internal.R.id.left_icon);
if (mLeftIcon != null) {
- mLeftIcon.setTag(ImageTransformState.ICON_TAG,
- notification.getNotification().getLargeIcon());
+ mLeftIcon.setTag(ImageTransformState.ICON_TAG, getLargeIcon(sbn.getNotification()));
}
mTitle = mView.findViewById(com.android.internal.R.id.title);
mText = mView.findViewById(com.android.internal.R.id.text);
@@ -171,6 +173,27 @@
updatePendingIntentCancellations();
}
+ @Nullable
+ protected final Icon getLargeIcon(Notification n) {
+ Icon modernLargeIcon = n.getLargeIcon();
+ if (modernLargeIcon == null && n.largeIcon != null) {
+ return Icon.createWithBitmap(n.largeIcon);
+ }
+ return modernLargeIcon;
+ }
+
+ @Nullable
+ protected final Icon getRightIcon(Notification n) {
+ if (n.extras.getBoolean(Notification.EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED)
+ && n.getNotificationStyle() == Notification.BigPictureStyle.class) {
+ Icon pictureIcon = Notification.BigPictureStyle.getPictureIcon(n.extras);
+ if (pictureIcon != null) {
+ return pictureIcon;
+ }
+ }
+ return getLargeIcon(n);
+ }
+
private void updatePendingIntentCancellations() {
if (mActions != null) {
int numActions = mActions.getChildCount();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 120f973..786fe2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -664,7 +664,7 @@
mDebugPaint.setColor(Color.CYAN);
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
- y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight());
+ y = (int) (mAmbientState.getStackY() + mSidePaddings + mAmbientState.getStackHeight());
mDebugPaint.setColor(Color.BLUE);
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
}
@@ -1148,12 +1148,15 @@
if (mOnStackYChanged != null) {
mOnStackYChanged.run();
}
-
- final float stackEndHeight = getHeight() - getEmptyBottomMargin() - mTopPadding;
- mAmbientState.setStackEndHeight(stackEndHeight);
- mAmbientState.setStackHeight(
- MathUtils.lerp(stackEndHeight * StackScrollAlgorithm.START_FRACTION,
- stackEndHeight, fraction));
+ if (mQsExpansionFraction <= 0) {
+ final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mSidePaddings;
+ final float stackEndHeight = Math.max(0f,
+ getHeight() - getEmptyBottomMargin() - stackY - scrimTopPadding);
+ mAmbientState.setStackEndHeight(stackEndHeight);
+ mAmbientState.setStackHeight(
+ MathUtils.lerp(stackEndHeight * StackScrollAlgorithm.START_FRACTION,
+ stackEndHeight, fraction));
+ }
}
void setOnStackYChanged(Runnable onStackYChanged) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index d94d030..b2d39a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -58,6 +58,7 @@
private int mStatusBarHeight;
private float mHeadsUpInset;
private int mPinnedZTranslationExtra;
+ private float mNotificationScrimPadding;
public StackScrollAlgorithm(
Context context,
@@ -82,6 +83,7 @@
mPinnedZTranslationExtra = res.getDimensionPixelSize(
R.dimen.heads_up_pinned_elevation);
mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
+ mNotificationScrimPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
}
/**
@@ -258,6 +260,9 @@
// expanded. Consider updating these states in updateContentView instead so that we don't
// have to recalculate in every frame.
float currentY = -scrollY;
+ if (!ambientState.isOnKeyguard()) {
+ currentY += mNotificationScrimPadding;
+ }
float previousY = 0;
state.firstViewInShelf = null;
state.viewHeightBeforeShelf = -1;
@@ -318,6 +323,9 @@
AmbientState ambientState) {
// The y coordinate of the current child.
float currentYPosition = -algorithmState.scrollY;
+ if (!ambientState.isOnKeyguard()) {
+ currentYPosition += mNotificationScrimPadding;
+ }
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index b9fe9c4a..c5a155e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -326,11 +326,13 @@
// Show the ongoing call chip only if there is an ongoing call *and* notification icons
// are allowed. (The ongoing call chip occupies the same area as the notification icons,
// so if the icons are disabled then the call chip should be, too.)
- if (hasOngoingCall && !disableNotifications) {
+ boolean showOngoingCallChip = hasOngoingCall && !disableNotifications;
+ if (showOngoingCallChip) {
showOngoingCallChip(animate);
} else {
hideOngoingCallChip(animate);
}
+ mOngoingCallController.notifyChipVisibilityChanged(showOngoingCallChip);
}
private boolean shouldHideNotificationIcons() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 71ba091..c64b893 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -929,7 +929,7 @@
}
GetWalletCardsRequest request =
new GetWalletCardsRequest(1 /* cardWidth */, 1 /* cardHeight */,
- 1 /* iconSizePx */, 2 /* maxCards */);
+ 1 /* iconSizePx */, 1 /* maxCards */);
mQuickAccessWalletClient.getWalletCards(mUiExecutor, request, mCardRetriever);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 16abd12..075a0c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -2072,14 +2072,13 @@
final int qsPanelBottomY = calculateQsBottomPosition(getQsExpansionFraction());
final boolean visible = (getQsExpansionFraction() > 0 || qsPanelBottomY > 0)
&& !mShouldUseSplitNotificationShade;
- final float notificationTop = mAmbientState.getStackY()
- - mNotificationScrimPadding
- - mAmbientState.getScrollY();
+ final float notificationTop = mAmbientState.getStackY() - mAmbientState.getScrollY();
setQsExpansionEnabled(mAmbientState.getScrollY() == 0);
int radius = mScrimCornerRadius;
if (!mShouldUseSplitNotificationShade) {
- top = (int) Math.min(qsPanelBottomY, notificationTop);
+ top = (int) (isOnKeyguard() ? Math.min(qsPanelBottomY, notificationTop)
+ : notificationTop);
bottom = getView().getBottom();
left = getView().getLeft();
right = getView().getRight();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
index 1fe77fd..6e27cae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
@@ -56,6 +56,7 @@
// call starts.
minimumTextWidth = 0
shouldHideText = false
+ visibility = VISIBLE
super.setBase(base)
}
@@ -76,6 +77,9 @@
if (desiredTextWidth > enforcedTextWidth) {
shouldHideText = true
+ // Changing visibility ensures that the content description is not read aloud when the
+ // time isn't displayed.
+ visibility = GONE
setMeasuredDimension(0, 0)
} else {
// It's possible that the current text could fit in a smaller width, but we don't want
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 6d1df5b..e9d256c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -49,7 +49,8 @@
private val systemClock: SystemClock,
private val activityStarter: ActivityStarter,
@Main private val mainExecutor: Executor,
- private val iActivityManager: IActivityManager
+ private val iActivityManager: IActivityManager,
+ private val logger: OngoingCallLogger
) : CallbackController<OngoingCallListener> {
/** Null if there's no ongoing call. */
@@ -104,7 +105,7 @@
/**
* Sets the chip view that will contain ongoing call information.
*
- * Should only be called from [CollapedStatusBarFragment].
+ * Should only be called from [CollapsedStatusBarFragment].
*/
fun setChipView(chipView: ViewGroup) {
this.chipView = chipView
@@ -113,6 +114,16 @@
}
}
+
+ /**
+ * Called when the chip's visibility may have changed.
+ *
+ * Should only be called from [CollapsedStatusBarFragment].
+ */
+ fun notifyChipVisibilityChanged(chipIsVisible: Boolean) {
+ logger.logChipVisibilityChanged(chipIsVisible)
+ }
+
/**
* Returns true if there's an active ongoing call that should be displayed in a status bar chip.
*/
@@ -150,6 +161,7 @@
timeView.start()
currentChipView.setOnClickListener {
+ logger.logChipClicked()
activityStarter.postStartActivityDismissingKeyguard(
currentOngoingCallInfo.intent, 0,
ActivityLaunchAnimator.Controller.fromView(it))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt
new file mode 100644
index 0000000..177f215
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.ongoingcall
+
+import androidx.annotation.VisibleForTesting
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** A class to log events for the ongoing call chip. */
+@SysUISingleton
+class OngoingCallLogger @Inject constructor(private val logger: UiEventLogger) {
+
+ private var chipIsVisible: Boolean = false
+
+ /** Logs that the ongoing call chip was clicked. */
+ fun logChipClicked() {
+ logger.log(OngoingCallEvents.ONGOING_CALL_CLICKED)
+ }
+
+ /**
+ * If needed, logs that the ongoing call chip's visibility has changed.
+ *
+ * For now, only logs when the chip changes from not visible to visible.
+ */
+ fun logChipVisibilityChanged(chipIsVisible: Boolean) {
+ if (chipIsVisible && chipIsVisible != this.chipIsVisible) {
+ logger.log(OngoingCallEvents.ONGOING_CALL_VISIBLE)
+ }
+ this.chipIsVisible = chipIsVisible
+ }
+
+ @VisibleForTesting
+ enum class OngoingCallEvents(val metricId: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "The ongoing call chip became visible")
+ ONGOING_CALL_VISIBLE(813),
+
+ @UiEvent(doc = "The ongoing call chip was clicked")
+ ONGOING_CALL_CLICKED(814);
+
+ override fun getId() = metricId
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 82ad00a..2e75395 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -30,6 +30,8 @@
import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.AlertingNotificationManager;
@@ -60,9 +62,28 @@
private final ArrayMap<String, Long> mSnoozedPackages;
private final AccessibilityManagerWrapper mAccessibilityMgr;
+ private final UiEventLogger mUiEventLogger;
+
+ /**
+ * Enum entry for notification peek logged from this class.
+ */
+ enum NotificationPeekEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Heads-up notification peeked on screen.")
+ NOTIFICATION_PEEK(801);
+
+ private final int mId;
+ NotificationPeekEvent(int id) {
+ mId = id;
+ }
+ @Override public int getId() {
+ return mId;
+ }
+ }
+
public HeadsUpManager(@NonNull final Context context) {
mContext = context;
mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class);
+ mUiEventLogger = Dependency.get(UiEventLogger.class);
Resources resources = context.getResources();
mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
mAutoDismissNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
@@ -130,6 +151,11 @@
if (entry.isRowPinned() != isPinned) {
entry.setRowPinned(isPinned);
updatePinnedMode();
+ if (isPinned && entry.getSbn() != null) {
+ mUiEventLogger.logWithInstanceId(
+ NotificationPeekEvent.NOTIFICATION_PEEK, entry.getSbn().getUid(),
+ entry.getSbn().getPackageName(), entry.getSbn().getInstanceId());
+ }
for (OnHeadsUpChangedListener listener : mListeners) {
if (isPinned) {
listener.onHeadsUpPinned(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index 3c1e123..865aa23f 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -66,6 +66,13 @@
"android.theme.customization.accent_color";
static final String OVERLAY_CATEGORY_SYSTEM_PALETTE =
"android.theme.customization.system_palette";
+
+ static final String OVERLAY_COLOR_SOURCE = "android.theme.customization.color_source";
+
+ static final String COLOR_SOURCE_PRESET = "preset";
+
+ static final String TIMESTAMP_FIELD = "_applied_timestamp";
+
@VisibleForTesting
static final String OVERLAY_CATEGORY_FONT = "android.theme.customization.font";
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 9bdd8c0..195114f 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -15,8 +15,11 @@
*/
package com.android.systemui.theme;
+import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_PRESET;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE;
+import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_SOURCE;
+import static com.android.systemui.theme.ThemeOverlayApplier.TIMESTAMP_FIELD;
import android.annotation.Nullable;
import android.app.WallpaperColors;
@@ -90,12 +93,12 @@
private final UserManager mUserManager;
private final BroadcastDispatcher mBroadcastDispatcher;
private final Executor mBgExecutor;
- private final SecureSettings mSecureSettings;
+ private SecureSettings mSecureSettings;
private final Executor mMainExecutor;
private final Handler mBgHandler;
private final WallpaperManager mWallpaperManager;
private final boolean mIsMonetEnabled;
- private final UserTracker mUserTracker;
+ private UserTracker mUserTracker;
private DeviceProvisionedController mDeviceProvisionedController;
private WallpaperColors mSystemColors;
// If fabricated overlays were already created for the current theme.
@@ -112,6 +115,8 @@
private boolean mAcceptColorEvents = true;
// Defers changing themes until Setup Wizard is done.
private boolean mDeferredThemeEvaluation;
+ // Determines if we should ignore THEME_CUSTOMIZATION_OVERLAY_PACKAGES setting changes.
+ private boolean mSkipSettingChange;
private final DeviceProvisionedListener mDeviceProvisionedListener =
new DeviceProvisionedListener() {
@@ -162,6 +167,35 @@
}
}
}
+ // Check if we need to reset to default colors (if a color override was set that is sourced
+ // from the wallpaper)
+ int currentUser = mUserTracker.getUserId();
+ String overlayPackageJson = mSecureSettings.getStringForUser(
+ Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
+ currentUser);
+ if (!TextUtils.isEmpty(overlayPackageJson)) {
+ try {
+ JSONObject jsonObject = new JSONObject(overlayPackageJson);
+ if ((jsonObject.has(OVERLAY_CATEGORY_ACCENT_COLOR)
+ || jsonObject.has(OVERLAY_CATEGORY_SYSTEM_PALETTE))
+ && !COLOR_SOURCE_PRESET.equals(
+ jsonObject.optString(OVERLAY_COLOR_SOURCE))) {
+ mSkipSettingChange = true;
+ jsonObject.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
+ jsonObject.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
+ jsonObject.remove(OVERLAY_COLOR_SOURCE);
+ jsonObject.put(TIMESTAMP_FIELD, System.currentTimeMillis());
+ if (DEBUG) {
+ Log.d(TAG, "Updating theme setting from "
+ + overlayPackageJson + " to " + jsonObject.toString());
+ }
+ mSecureSettings.putString(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
+ jsonObject.toString());
+ }
+ } catch (JSONException e) {
+ Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e);
+ }
+ }
reevaluateSystemTheme(false /* forceReload */);
};
@@ -232,6 +266,11 @@
mDeferredThemeEvaluation = true;
return;
}
+ if (mSkipSettingChange) {
+ if (DEBUG) Log.d(TAG, "Skipping setting change");
+ mSkipSettingChange = false;
+ return;
+ }
reevaluateSystemTheme(true /* forceReload */);
}
},
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java
index 2270f96..7a5ceb5 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java
@@ -16,6 +16,7 @@
package com.android.systemui.util.concurrency;
+import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.Executor;
@@ -29,6 +30,14 @@
*/
public interface ThreadFactory {
/**
+ * Returns a {@link Handler} running on a named thread.
+ *
+ * The thread is implicitly started and may be left running indefinitely, depending on the
+ * implementation. Assume this is the case and use responsibly.
+ */
+ Handler builderHandlerOnNewThread(String threadName);
+
+ /**
* Return an {@link java.util.concurrent.Executor} running on a named thread.
*
* The thread is implicitly started and may be left running indefinitely, depending on the
@@ -45,6 +54,11 @@
DelayableExecutor buildDelayableExecutorOnNewThread(String threadName);
/**
+ * Return an {@link DelayableExecutor} running on the given HandlerThread.
+ **/
+ DelayableExecutor buildDelayableExecutorOnHandler(Handler handler);
+
+ /**
* Return an {@link DelayableExecutor} running the given Looper
**/
DelayableExecutor buildDelayableExecutorOnLooper(Looper looper);
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java
index 2d9f2b4..184b831 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java
@@ -16,6 +16,7 @@
package com.android.systemui.util.concurrency;
+import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -27,16 +28,31 @@
@Inject
ThreadFactoryImpl() {}
+ @Override
+ public Handler builderHandlerOnNewThread(String threadName) {
+ HandlerThread handlerThread = new HandlerThread(threadName);
+ handlerThread.start();
+ return new Handler(handlerThread.getLooper());
+ }
+
+ @Override
public Executor buildExecutorOnNewThread(String threadName) {
return buildDelayableExecutorOnNewThread(threadName);
}
+ @Override
public DelayableExecutor buildDelayableExecutorOnNewThread(String threadName) {
HandlerThread handlerThread = new HandlerThread(threadName);
handlerThread.start();
- return new ExecutorImpl(handlerThread.getLooper());
+ return buildDelayableExecutorOnLooper(handlerThread.getLooper());
}
+ @Override
+ public DelayableExecutor buildDelayableExecutorOnHandler(Handler handler) {
+ return buildDelayableExecutorOnLooper(handler.getLooper());
+ }
+
+ @Override
public DelayableExecutor buildDelayableExecutorOnLooper(Looper looper) {
return new ExecutorImpl(looper);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
index ec62981..b57d937 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
@@ -234,7 +234,9 @@
mWalletView.show();
mWalletView.hideErrorMessage();
int iconSizePx =
- mContext.getResources().getDimensionPixelSize(R.dimen.wallet_view_header_icon_size);
+ mContext
+ .getResources()
+ .getDimensionPixelSize(R.dimen.wallet_screen_header_icon_size);
GetWalletCardsRequest request =
new GetWalletCardsRequest(cardWidthPx, cardHeightPx, iconSizePx, MAX_CARDS);
mWalletClient.getWalletCards(mExecutor, request, this);
@@ -340,7 +342,11 @@
@Override
public CharSequence getLabel() {
- return mWalletCard.getCardLabel();
+ CharSequence label = mWalletCard.getCardLabel();
+ if (label == null) {
+ return "";
+ }
+ return label;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
index dbb99d3..e42ce6a 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -134,7 +134,8 @@
mCardCarouselContainer.setVisibility(VISIBLE);
mErrorView.setVisibility(GONE);
mEmptyStateView.setVisibility(GONE);
- renderHeaderIconAndActionButton(data.get(selectedIndex), isDeviceLocked);
+ mIcon.setImageDrawable(getHeaderIcon(mContext, data.get(selectedIndex)));
+ renderActionButton(data.get(selectedIndex), isDeviceLocked);
if (shouldAnimate) {
animateViewsShown(mIcon, mCardLabel, mActionButton);
}
@@ -221,17 +222,6 @@
return mCardLabel;
}
- private void renderHeaderIconAndActionButton(WalletCardViewInfo walletCard, boolean isLocked) {
- Drawable icon = getHeaderIcon(mContext, walletCard);
- if (icon != null) {
- mIcon.setImageDrawable(walletCard.getIcon());
- mIcon.setVisibility(VISIBLE);
- } else {
- mIcon.setVisibility(INVISIBLE);
- }
- renderActionButton(walletCard, isLocked);
- }
-
@Nullable
private static Drawable getHeaderIcon(Context context, WalletCardViewInfo walletCard) {
Drawable icon = walletCard.getIcon();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 8c5f74d..98467d4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -19,30 +19,20 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.smartspace.SmartspaceTarget;
-import android.content.Context;
-import android.content.pm.UserInfo;
import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
-import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
-import androidx.annotation.Nullable;
+import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
@@ -50,21 +40,14 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.BcSmartspaceDataPlugin;
-import com.android.systemui.plugins.BcSmartspaceDataPlugin.IntentStarter;
import com.android.systemui.plugins.ClockPlugin;
-import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.util.settings.SecureSettings;
import org.junit.Before;
import org.junit.Test;
@@ -74,78 +57,54 @@
import org.mockito.MockitoAnnotations;
import org.mockito.verification.VerificationMode;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
@Mock
+ private KeyguardClockSwitch mView;
+ @Mock
private StatusBarStateController mStatusBarStateController;
@Mock
private SysuiColorExtractor mColorExtractor;
@Mock
private ClockManager mClockManager;
@Mock
- private KeyguardClockSwitch mView;
- @Mock
- private NotificationIconContainer mNotificationIcons;
- @Mock
- private ClockPlugin mClockPlugin;
- @Mock
- ColorExtractor.GradientColors mGradientColors;
- @Mock
KeyguardSliceViewController mKeyguardSliceViewController;
@Mock
- Resources mResources;
- @Mock
NotificationIconAreaController mNotificationIconAreaController;
@Mock
BroadcastDispatcher mBroadcastDispatcher;
@Mock
- private FeatureFlags mFeatureFlags;
+ BatteryController mBatteryController;
@Mock
- private Executor mExecutor;
+ KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ KeyguardBypassController mBypassController;
+ @Mock
+ LockscreenSmartspaceController mSmartspaceController;
+
+ @Mock
+ Resources mResources;
+ @Mock
+ private ClockPlugin mClockPlugin;
+ @Mock
+ ColorExtractor.GradientColors mGradientColors;
+
+ @Mock
+ private NotificationIconContainer mNotificationIcons;
@Mock
private AnimatableClockView mClockView;
@Mock
private AnimatableClockView mLargeClockView;
@Mock
private FrameLayout mLargeClockFrame;
- @Mock
- BatteryController mBatteryController;
- @Mock
- ConfigurationController mConfigurationController;
- @Mock
- Optional<BcSmartspaceDataPlugin> mOptionalSmartspaceDataProvider;
- @Mock
- BcSmartspaceDataPlugin mSmartspaceDataProvider;
- @Mock
- SmartspaceView mSmartspaceView;
- @Mock
- ActivityStarter mActivityStarter;
- @Mock
- FalsingManager mFalsingManager;
- @Mock
- KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock
- KeyguardBypassController mBypassController;
- @Mock
- Handler mHandler;
- @Mock
- UserTracker mUserTracker;
- @Mock
- SecureSettings mSecureSettings;
+
+ private final View mFakeSmartspaceView = new View(mContext);
private KeyguardClockSwitchController mController;
private View mStatusArea;
- private static final int USER_ID = 5;
- private static final int MANAGED_USER_ID = 15;
-
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -162,9 +121,9 @@
when(mClockView.getContext()).thenReturn(getContext());
when(mLargeClockView.getContext()).thenReturn(getContext());
- when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true);
when(mView.isAttachedToWindow()).thenReturn(true);
when(mResources.getString(anyInt())).thenReturn("h:mm");
+ when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
mController = new KeyguardClockSwitchController(
mView,
mStatusBarStateController,
@@ -173,28 +132,16 @@
mKeyguardSliceViewController,
mNotificationIconAreaController,
mBroadcastDispatcher,
- mFeatureFlags,
- mExecutor,
mBatteryController,
- mConfigurationController,
- mActivityStarter,
- mFalsingManager,
mKeyguardUpdateMonitor,
mBypassController,
- mHandler,
- mUserTracker,
- mSecureSettings,
- mOptionalSmartspaceDataProvider
- );
+ mSmartspaceController);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
mStatusArea = new View(getContext());
when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(mStatusArea);
- when(mOptionalSmartspaceDataProvider.isPresent()).thenReturn(true);
- when(mOptionalSmartspaceDataProvider.get()).thenReturn(mSmartspaceDataProvider);
- when(mSmartspaceDataProvider.getView(any())).thenReturn(mSmartspaceView);
}
@Test
@@ -255,119 +202,34 @@
@Test
public void testSmartspaceEnabledRemovesKeyguardStatusArea() {
- when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true);
+ when(mSmartspaceController.isEnabled()).thenReturn(true);
+ when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
mController.init();
assertEquals(View.GONE, mStatusArea.getVisibility());
}
@Test
- public void testSmartspaceEnabledNoDataProviderShowsKeyguardStatusArea() {
- when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true);
- when(mOptionalSmartspaceDataProvider.isPresent()).thenReturn(false);
- mController.init();
-
- assertEquals(View.VISIBLE, mStatusArea.getVisibility());
- }
-
- @Test
public void testSmartspaceDisabledShowsKeyguardStatusArea() {
- when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(false);
+ when(mSmartspaceController.isEnabled()).thenReturn(false);
mController.init();
assertEquals(View.VISIBLE, mStatusArea.getVisibility());
}
@Test
- public void testThemeChangeNotifiesSmartspace() {
+ public void testDetachRemovesSmartspaceView() {
+ when(mSmartspaceController.isEnabled()).thenReturn(true);
+ when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
mController.init();
- verify(mSmartspaceView).setPrimaryTextColor(anyInt());
+ verify(mView).addView(eq(mFakeSmartspaceView), anyInt(), any());
- mController.getConfigurationListener().onThemeChanged();
- verify(mSmartspaceView, times(2)).setPrimaryTextColor(anyInt());
- }
+ ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
+ verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture());
- @Test
- public void doNotFilterRegularTarget() {
- setupPrimaryAndManagedUser();
- mController.init();
-
- when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(USER_ID))).thenReturn(0);
- when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(MANAGED_USER_ID)))
- .thenReturn(0);
-
- mController.getSettingsObserver().onChange(true, null);
-
- SmartspaceTarget t = mock(SmartspaceTarget.class);
- when(t.isSensitive()).thenReturn(false);
- when(t.getUserHandle()).thenReturn(new UserHandle(USER_ID));
- assertEquals(false, mController.filterSmartspaceTarget(t));
-
- reset(t);
- when(t.isSensitive()).thenReturn(false);
- when(t.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID));
- assertEquals(false, mController.filterSmartspaceTarget(t));
- }
-
- @Test
- public void filterAllSensitiveTargetsAllUsers() {
- setupPrimaryAndManagedUser();
- mController.init();
-
- when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(USER_ID))).thenReturn(0);
- when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(MANAGED_USER_ID)))
- .thenReturn(0);
-
- mController.getSettingsObserver().onChange(true, null);
-
- SmartspaceTarget t = mock(SmartspaceTarget.class);
- when(t.isSensitive()).thenReturn(true);
- when(t.getUserHandle()).thenReturn(new UserHandle(USER_ID));
- assertEquals(true, mController.filterSmartspaceTarget(t));
-
- reset(t);
- when(t.isSensitive()).thenReturn(true);
- when(t.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID));
- assertEquals(true, mController.filterSmartspaceTarget(t));
- }
-
- @Test
- public void filterSensitiveManagedUserTargets() {
- setupPrimaryAndManagedUser();
- mController.init();
-
- when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(USER_ID))).thenReturn(1);
- when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(MANAGED_USER_ID)))
- .thenReturn(0);
-
- mController.getSettingsObserver().onChange(true, null);
-
- SmartspaceTarget t = mock(SmartspaceTarget.class);
- when(t.isSensitive()).thenReturn(true);
- when(t.getUserHandle()).thenReturn(new UserHandle(USER_ID));
- assertEquals(false, mController.filterSmartspaceTarget(t));
-
- reset(t);
- when(t.isSensitive()).thenReturn(true);
- when(t.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID));
- assertEquals(true, mController.filterSmartspaceTarget(t));
- }
-
- private void setupPrimaryAndManagedUser() {
- UserInfo userInfo = mock(UserInfo.class);
- when(userInfo.isManagedProfile()).thenReturn(true);
- when(userInfo.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID));
- when(mUserTracker.getUserProfiles()).thenReturn(List.of(userInfo));
-
- when(mUserTracker.getUserId()).thenReturn(USER_ID);
- when(mUserTracker.getUserHandle()).thenReturn(new UserHandle(USER_ID));
- }
-
- private void setupPrimaryAndNoManagedUser() {
- when(mUserTracker.getUserProfiles()).thenReturn(Collections.emptyList());
-
- when(mUserTracker.getUserId()).thenReturn(USER_ID);
- when(mUserTracker.getUserHandle()).thenReturn(new UserHandle(USER_ID));
+ listenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView);
+ verify(mView).removeView(mFakeSmartspaceView);
}
private void verifyAttachment(VerificationMode times) {
@@ -377,25 +239,4 @@
any(ColorExtractor.OnColorsChangedListener.class));
verify(mView, times).updateColors(mGradientColors);
}
-
- private static class SmartspaceView extends View
- implements BcSmartspaceDataPlugin.SmartspaceView {
- SmartspaceView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public void registerDataProvider(BcSmartspaceDataPlugin plugin) { }
-
- public void setPrimaryTextColor(int color) { }
-
- public void setDozeAmount(float amount) { }
-
- public void setIntentStarter(IntentStarter intentStarter) { }
-
- public void setFalsingManager(FalsingManager falsingManager) { }
-
- public void setDnd(@Nullable Drawable dndIcon, @Nullable String description) { }
-
- public void setNextAlarm(@Nullable Drawable dndIcon, @Nullable String description) { }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index b9ce203..ed5cbe2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -67,7 +67,6 @@
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
-import com.android.systemui.util.concurrency.ThreadFactory;
import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
@@ -87,13 +86,12 @@
private static final Rect ZERO_RECT = new Rect();
- private TestableLooper mTestableLooper;
private ScreenDecorations mScreenDecorations;
private WindowManager mWindowManager;
private DisplayManager mDisplayManager;
private SecureSettings mSecureSettings;
- private Handler mMainHandler;
- private ThreadFactory mThreadFactory;
+ private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+ private FakeThreadFactory mThreadFactory;
@Mock
private TunerService mTunerService;
@Mock
@@ -107,10 +105,10 @@
public void setup() {
MockitoAnnotations.initMocks(this);
- mTestableLooper = TestableLooper.get(this);
- mMainHandler = new Handler(mTestableLooper.getLooper());
+ Handler mainHandler = new Handler(TestableLooper.get(this).getLooper());
mSecureSettings = new FakeSettings();
- mThreadFactory = new FakeThreadFactory(new FakeExecutor(new FakeSystemClock()));
+ mThreadFactory = new FakeThreadFactory(mExecutor);
+ mThreadFactory.setHandler(mainHandler);
mWindowManager = mock(WindowManager.class);
WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
@@ -124,30 +122,25 @@
when(mDisplayManager.getDisplay(anyInt())).thenReturn(display);
mContext.addMockSystemService(DisplayManager.class, mDisplayManager);
- mScreenDecorations = spy(new ScreenDecorations(mContext, mMainHandler, mSecureSettings,
+ mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings,
mBroadcastDispatcher, mTunerService, mUserTracker, mDotViewController,
mThreadFactory) {
@Override
public void start() {
super.start();
- mTestableLooper.processAllMessages();
- }
-
- @Override
- Handler startHandlerThread() {
- return new Handler(mTestableLooper.getLooper());
+ mExecutor.runAllReady();
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- mTestableLooper.processAllMessages();
+ mExecutor.runAllReady();
}
@Override
public void onTuningChanged(String key, String newValue) {
super.onTuningChanged(key, newValue);
- mTestableLooper.processAllMessages();
+ mExecutor.runAllReady();
}
});
reset(mTunerService);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index dd3a192..e62fa91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -289,6 +289,7 @@
captor.value.onLongClick(holder.player)
verify(mediaViewController, never()).openGuts()
+ verify(mediaViewController).closeGuts(false)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index 406f40c..a974421 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -24,6 +24,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -73,11 +74,15 @@
@Mock
private lateinit var mediaCarouselController: MediaCarouselController
@Mock
+ private lateinit var mediaCarouselScrollHandler: MediaCarouselScrollHandler
+ @Mock
private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock
private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Captor
private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
+ @Captor
+ private lateinit var statusBarCallback: ArgumentCaptor<(StatusBarStateController.StateListener)>
@JvmField
@Rule
val mockito = MockitoJUnit.rule()
@@ -96,10 +101,13 @@
wakefulnessLifecycle,
statusBarKeyguardViewManager)
verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
+ verify(statusBarStateController).addCallback(statusBarCallback.capture())
setupHost(lockHost, MediaHierarchyManager.LOCATION_LOCKSCREEN)
setupHost(qsHost, MediaHierarchyManager.LOCATION_QS)
setupHost(qqsHost, MediaHierarchyManager.LOCATION_QQS)
`when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
+ `when`(mediaCarouselController.mediaCarouselScrollHandler)
+ .thenReturn(mediaCarouselScrollHandler)
// We'll use the viewmanager to verify a few calls below, let's reset this.
clearInvocations(mediaCarouselController)
}
@@ -153,4 +161,11 @@
verify(mediaCarouselController).closeGuts()
}
+
+ @Test
+ fun testCloseGutsWhenDoze() {
+ statusBarCallback.value.onDozingChanged(true)
+
+ verify(mediaCarouselController).closeGuts()
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
index 5c70a4ef2..7172307 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.people;
import static android.app.Notification.CATEGORY_MISSED_CALL;
+import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
import static android.app.people.ConversationStatus.ACTIVITY_GAME;
import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY;
@@ -25,6 +26,7 @@
import static android.app.people.PeopleSpaceTile.SHOW_CONTACTS;
import static android.app.people.PeopleSpaceTile.SHOW_IMPORTANT_CONVERSATIONS;
import static android.app.people.PeopleSpaceTile.SHOW_STARRED_CONTACTS;
+import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
import static com.android.systemui.people.PeopleSpaceUtils.STARRED_CONTACT;
@@ -127,6 +129,9 @@
.build();
@Mock
+ private Icon mIcon;
+
+ @Mock
private Context mMockContext;
@Mock
private PackageManager mPackageManager;
@@ -169,7 +174,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
PERSON_TILE_WITHOUT_NOTIFICATION, mOptions).getViews();
@@ -218,7 +223,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithLastInteraction, mOptions).getViews();
@@ -278,7 +283,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithAvailabilityAndNewStory, mOptions).getViews();
@@ -340,7 +345,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithStatusTemplate, mOptions).getViews();
@@ -376,6 +381,8 @@
assertEquals(name.getText(), NAME);
assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
assertEquals(View.VISIBLE, result.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.GONE, result.findViewById(R.id.scrim_layout).getVisibility());
+ assertEquals(View.GONE, result.findViewById(R.id.image).getVisibility());
// Has availability.
assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
// Has person icon.
@@ -403,7 +410,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithStatusTemplate, mOptions).getViews();
@@ -413,6 +420,8 @@
assertEquals(name.getText(), NAME);
assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
assertEquals(View.VISIBLE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.GONE, largeResult.findViewById(R.id.scrim_layout).getVisibility());
+ assertEquals(View.GONE, largeResult.findViewById(R.id.image).getVisibility());
// Has availability.
assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
// Has person icon.
@@ -426,6 +435,55 @@
}
@Test
+ public void testCreateRemoteViewsWithStatusTemplateWithImageOnMediumAndLarge() {
+ PeopleSpaceTile tileWithIconInStatusTemplate =
+ PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
+ Arrays.asList(new ConversationStatus.Builder(PERSON_TILE.getId(),
+ ACTIVITY_ANNIVERSARY).setDescription("Anniversary").setAvailability(
+ AVAILABILITY_AVAILABLE).setIcon(mIcon).build())).build();
+ RemoteViews views = getPeopleTileViewHelper(
+ tileWithIconInStatusTemplate, mOptions).getViews();
+ View result = views.apply(mContext, null);
+
+ assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
+ assertEquals(View.VISIBLE, result.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.VISIBLE, result.findViewById(R.id.scrim_layout).getVisibility());
+ assertEquals(View.GONE, result.findViewById(R.id.image).getVisibility());
+ // Has availability.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
+ // Has status.
+ TextView statusContent = (TextView) result.findViewById(R.id.name);
+ assertEquals(statusContent.getText(), "Anniversary");
+ assertThat(statusContent.getMaxLines()).isEqualTo(1);
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_width_for_large));
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
+ getSizeInDp(R.dimen.required_height_for_large));
+ RemoteViews largeView = getPeopleTileViewHelper(
+ tileWithIconInStatusTemplate, mOptions).getViews();
+ View largeResult = largeView.apply(mContext, null);
+
+ assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
+ assertEquals(View.GONE, largeResult.findViewById(R.id.name).getVisibility());
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.scrim_layout).getVisibility());
+ assertEquals(View.GONE, largeResult.findViewById(R.id.image).getVisibility());
+ // Has availability.
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
+ // Has person icon.
+ View personIcon = largeResult.findViewById(R.id.person_icon);
+ assertEquals(View.VISIBLE, personIcon.getVisibility());
+ // Has status content.
+ statusContent = (TextView) largeResult.findViewById(R.id.text_content);
+ assertEquals(View.VISIBLE, statusContent.getVisibility());
+ assertEquals(statusContent.getText(), "Anniversary");
+ assertThat(statusContent.getMaxLines()).isEqualTo(2);
+ }
+
+ @Test
public void testCreateRemoteViewsWithPackageSuspended() {
PeopleSpaceTile tile = PERSON_TILE.toBuilder()
.setIsPackageSuspended(true)
@@ -434,7 +492,7 @@
tile, mOptions).getViews();
View result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
}
@Test
@@ -446,7 +504,7 @@
tile, mOptions).getViews();
View result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_work_profile_quiet_layout);
}
@Test
@@ -458,7 +516,7 @@
tileWithDndBlocking, mOptions).getViews();
View result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(BLOCK_CONVERSATIONS)
@@ -468,7 +526,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_IMPORTANT_CONVERSATIONS)
@@ -477,7 +535,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_IMPORTANT_CONVERSATIONS)
@@ -487,7 +545,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_STARRED_CONTACTS)
@@ -497,7 +555,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_STARRED_CONTACTS)
@@ -507,7 +565,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_CONTACTS)
@@ -517,7 +575,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_CONTACTS)
@@ -527,7 +585,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_CONTACTS)
@@ -536,7 +594,7 @@
tileWithDndBlocking, mOptions).getViews();
result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+ assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
}
@Test
@@ -581,7 +639,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithMissedCallNotification, mOptions).getViews();
@@ -617,6 +675,7 @@
assertEquals(name.getText(), NAME);
assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
assertEquals(View.GONE, result.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.GONE, result.findViewById(R.id.scrim_layout).getVisibility());
// Has availability.
assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
// Has person icon.
@@ -649,7 +708,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithStatusAndNotification, mOptions).getViews();
@@ -659,6 +718,7 @@
assertEquals(name.getText(), NAME);
assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
assertEquals(View.GONE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
+ assertEquals(View.GONE, largeResult.findViewById(R.id.scrim_layout).getVisibility());
// Has availability.
assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
// Has person icon.
@@ -725,7 +785,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithStatusAndNotification, mOptions).getViews();
@@ -802,7 +862,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = getPeopleTileViewHelper(
tileWithStatusAndNotification, mOptions).getViews();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
index c050b62..ba2b37c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
@@ -30,6 +30,7 @@
import android.testing.ViewUtils;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
@@ -58,24 +59,26 @@
private DetailAdapter mMockDetailAdapter;
private TestableLooper mTestableLooper;
private UiEventLoggerFake mUiEventLogger;
+ private FrameLayout mParent;
@Before
public void setup() throws Exception {
mTestableLooper = TestableLooper.get(this);
mUiEventLogger = QSEvents.INSTANCE.setLoggerForTesting();
- mTestableLooper.runWithLooper(() -> {
- mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
- mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class);
- mQsDetail = (QSDetail) LayoutInflater.from(mContext).inflate(R.layout.qs_detail, null);
- mQsPanelController = mock(QSPanelController.class);
- mQuickHeader = mock(QuickStatusBarHeader.class);
- mQsDetail.setQsPanel(mQsPanelController, mQuickHeader, mock(QSFooter.class));
+ mParent = new FrameLayout(mContext);
+ mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
+ mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class);
+ LayoutInflater.from(mContext).inflate(R.layout.qs_detail, mParent);
+ mQsDetail = (QSDetail) mParent.getChildAt(0);
- mMockDetailAdapter = mock(DetailAdapter.class);
- when(mMockDetailAdapter.createDetailView(any(), any(), any()))
- .thenReturn(mock(View.class));
- });
+ mQsPanelController = mock(QSPanelController.class);
+ mQuickHeader = mock(QuickStatusBarHeader.class);
+ mQsDetail.setQsPanel(mQsPanelController, mQuickHeader, mock(QSFooter.class));
+
+ mMockDetailAdapter = mock(DetailAdapter.class);
+ when(mMockDetailAdapter.createDetailView(any(), any(), any()))
+ .thenReturn(new View(mContext));
// Only detail in use is the user detail
when(mMockDetailAdapter.openDetailEvent())
@@ -84,16 +87,18 @@
.thenReturn(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE);
when(mMockDetailAdapter.moreSettingsEvent())
.thenReturn(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS);
+ ViewUtils.attachView(mParent);
}
@After
public void tearDown() {
QSEvents.INSTANCE.resetLogger();
+ mTestableLooper.processAllMessages();
+ ViewUtils.detachView(mParent);
}
@Test
public void testShowDetail_Metrics() {
- ViewUtils.attachView(mQsDetail);
mTestableLooper.processAllMessages();
mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
@@ -107,14 +112,10 @@
assertEquals(1, mUiEventLogger.numLogs());
assertEquals(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE.getId(), mUiEventLogger.eventId(0));
-
- ViewUtils.detachView(mQsDetail);
- mTestableLooper.processAllMessages();
}
@Test
public void testMoreSettingsButton() {
- ViewUtils.attachView(mQsDetail);
mTestableLooper.processAllMessages();
mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
@@ -127,9 +128,6 @@
assertEquals(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS.getId(), mUiEventLogger.eventId(0));
verify(mActivityStarter).postStartActivityDismissingKeyguard(any(), anyInt());
-
- ViewUtils.detachView(mQsDetail);
- mTestableLooper.processAllMessages();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
new file mode 100644
index 0000000..5366858
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.lockscreen
+
+
+import android.app.smartspace.SmartspaceManager
+import android.app.smartspace.SmartspaceSession
+import android.app.smartspace.SmartspaceSession.OnTargetsAvailableListener
+import android.app.smartspace.SmartspaceTarget
+import android.content.ComponentName
+import android.content.ContentResolver
+import android.content.pm.UserInfo
+import android.database.ContentObserver
+import android.graphics.drawable.Drawable
+import android.net.Uri
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
+import android.view.View
+import android.widget.FrameLayout
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import com.android.systemui.util.concurrency.FakeExecution
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.Optional
+
+@SmallTest
+class LockscreenSmartspaceControllerTest : SysuiTestCase() {
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
+ @Mock
+ private lateinit var smartspaceManager: SmartspaceManager
+ @Mock
+ private lateinit var smartspaceSession: SmartspaceSession
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var falsingManager: FalsingManager
+ @Mock
+ private lateinit var secureSettings: SecureSettings
+ @Mock
+ private lateinit var userTracker: UserTracker
+ @Mock
+ private lateinit var contentResolver: ContentResolver
+ @Mock
+ private lateinit var configurationController: ConfigurationController
+ @Mock
+ private lateinit var statusBarStateController: StatusBarStateController
+ @Mock
+ private lateinit var handler: Handler
+
+ @Mock
+ private lateinit var plugin: BcSmartspaceDataPlugin
+ @Mock
+ private lateinit var controllerListener: SmartspaceTargetListener
+
+ @Captor
+ private lateinit var sessionListenerCaptor: ArgumentCaptor<OnTargetsAvailableListener>
+ @Captor
+ private lateinit var userTrackerCaptor: ArgumentCaptor<UserTracker.Callback>
+ @Captor
+ private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
+ @Captor
+ private lateinit var configChangeListenerCaptor: ArgumentCaptor<ConfigurationListener>
+ @Captor
+ private lateinit var statusBarStateListenerCaptor: ArgumentCaptor<StateListener>
+
+ private lateinit var sessionListener: OnTargetsAvailableListener
+ private lateinit var userListener: UserTracker.Callback
+ private lateinit var settingsObserver: ContentObserver
+ private lateinit var configChangeListener: ConfigurationListener
+ private lateinit var statusBarStateListener: StateListener
+
+ private val clock = FakeSystemClock()
+ private val executor = FakeExecutor(clock)
+ private val execution = FakeExecution()
+ private val fakeParent = FrameLayout(context)
+ private val fakePrivateLockscreenSettingUri = Uri.Builder().appendPath("test").build()
+
+ private val userHandlePrimary: UserHandle = UserHandle(0)
+ private val userHandleManaged: UserHandle = UserHandle(2)
+ private val userHandleSecondary: UserHandle = UserHandle(3)
+
+ private val userList = listOf(
+ mockUserInfo(userHandlePrimary, isManagedProfile = false),
+ mockUserInfo(userHandleManaged, isManagedProfile = true),
+ mockUserInfo(userHandleSecondary, isManagedProfile = false)
+ )
+
+ private lateinit var controller: LockscreenSmartspaceController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ `when`(featureFlags.isSmartspaceEnabled).thenReturn(true)
+
+ `when`(secureSettings.getUriFor(PRIVATE_LOCKSCREEN_SETTING))
+ .thenReturn(fakePrivateLockscreenSettingUri)
+ `when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(smartspaceSession)
+ `when`(plugin.getView(any())).thenReturn(fakeSmartspaceView)
+ `when`(userTracker.userProfiles).thenReturn(userList)
+ `when`(statusBarStateController.dozeAmount).thenReturn(0.5f)
+
+ setActiveUser(userHandlePrimary)
+ setAllowPrivateNotifications(userHandlePrimary, true)
+ setAllowPrivateNotifications(userHandleManaged, true)
+ setAllowPrivateNotifications(userHandleSecondary, true)
+
+ controller = LockscreenSmartspaceController(
+ context,
+ featureFlags,
+ smartspaceManager,
+ activityStarter,
+ falsingManager,
+ secureSettings,
+ userTracker,
+ contentResolver,
+ configurationController,
+ statusBarStateController,
+ execution,
+ executor,
+ handler,
+ Optional.of(plugin)
+ )
+ }
+
+ @Test(expected = RuntimeException::class)
+ fun testThrowsIfFlagIsDisabled() {
+ // GIVEN the feature flag is disabled
+ `when`(featureFlags.isSmartspaceEnabled).thenReturn(false)
+
+ // WHEN we try to build the view
+ controller.buildAndConnectView(fakeParent)
+
+ // THEN an exception is thrown
+ }
+
+ @Test
+ fun testListenersAreRegistered() {
+ // GIVEN a listener is added after a session is created
+ connectSession()
+
+ // WHEN a listener is registered
+ controller.addListener(controllerListener)
+
+ // THEN the listener is registered to the underlying plugin
+ verify(plugin).registerListener(controllerListener)
+ }
+
+ @Test
+ fun testEarlyRegisteredListenersAreAttachedAfterConnected() {
+ // GIVEN a listener that is registered before the session is created
+ controller.addListener(controllerListener)
+
+ // WHEN the session is created
+ connectSession()
+
+ // THEN the listener is subsequently registered
+ verify(plugin).registerListener(controllerListener)
+ }
+
+ @Test
+ fun testEmptyListIsEmittedAfterDisconnect() {
+ // GIVEN a registered listener on an active session
+ connectSession()
+ clearInvocations(plugin)
+
+ // WHEN the session is closed
+ controller.disconnect()
+
+ // THEN the listener receives an empty list of targets
+ verify(plugin).onTargetsAvailable(emptyList())
+ }
+
+ @Test
+ fun testUserChangeReloadsSmartspace() {
+ // GIVEN a connected smartspace session
+ connectSession()
+
+ // WHEN the active user changes
+ userListener.onUserChanged(-1, context)
+
+ // THEN we request a new smartspace update
+ verify(smartspaceSession).requestSmartspaceUpdate()
+ }
+
+ @Test
+ fun testSettingsChangeReloadsSmartspace() {
+ // GIVEN a connected smartspace session
+ connectSession()
+
+ // WHEN the lockscreen privacy setting changes
+ settingsObserver.onChange(true, null)
+
+ // THEN we request a new smartspace update
+ verify(smartspaceSession).requestSmartspaceUpdate()
+ }
+
+ @Test
+ fun testThemeChangeUpdatesTextColor() {
+ // GIVEN a connected smartspace session
+ connectSession()
+
+ // WHEN the theme changes
+ configChangeListener.onThemeChanged()
+
+ // We update the new text color to match the wallpaper color
+ verify(fakeSmartspaceView).setPrimaryTextColor(anyInt())
+ }
+
+ @Test
+ fun testDozeAmountChangeUpdatesView() {
+ // GIVEN a connected smartspace session
+ connectSession()
+
+ // WHEN the doze amount changes
+ statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f)
+
+ // We pass that along to the view
+ verify(fakeSmartspaceView).setDozeAmount(0.7f)
+ }
+
+ @Test
+ fun testSensitiveTargetsAreNotFilteredIfAllowed() {
+ // GIVEN the active and managed users allow sensitive content
+ connectSession()
+
+ // WHEN we receive a list of targets
+ val targets = listOf(
+ makeTarget(1, userHandlePrimary, isSensitive = true),
+ makeTarget(2, userHandleManaged, isSensitive = true),
+ makeTarget(3, userHandlePrimary, isSensitive = true)
+ )
+ sessionListener.onTargetsAvailable(targets)
+
+ // THEN all sensitive content is still shown
+ verify(plugin).onTargetsAvailable(eq(targets))
+ }
+
+ @Test
+ fun testNonSensitiveTargetsAreNeverFiltered() {
+ // GIVEN the active user doesn't allow sensitive lockscreen content
+ setAllowPrivateNotifications(userHandlePrimary, false)
+ connectSession()
+
+ // WHEN we receive a list of targets
+ val targets = listOf(
+ makeTarget(1, userHandlePrimary),
+ makeTarget(2, userHandlePrimary),
+ makeTarget(3, userHandlePrimary)
+ )
+ sessionListener.onTargetsAvailable(targets)
+
+ // THEN all non-sensitive content is still shown
+ verify(plugin).onTargetsAvailable(eq(targets))
+ }
+
+ @Test
+ fun testSensitiveTargetsAreFilteredOutForAppropriateUsers() {
+ // GIVEN the active and managed users don't allow sensitive lockscreen content
+ setAllowPrivateNotifications(userHandlePrimary, false)
+ setAllowPrivateNotifications(userHandleManaged, false)
+ connectSession()
+
+ // WHEN we receive a list of targets
+ val targets = listOf(
+ makeTarget(0, userHandlePrimary),
+ makeTarget(1, userHandlePrimary, isSensitive = true),
+ makeTarget(2, userHandleManaged, isSensitive = true),
+ makeTarget(3, userHandleManaged),
+ makeTarget(4, userHandlePrimary, isSensitive = true),
+ makeTarget(5, userHandlePrimary),
+ makeTarget(6, userHandleSecondary, isSensitive = true)
+ )
+ sessionListener.onTargetsAvailable(targets)
+
+ // THEN only non-sensitive content from those accounts is shown
+ verify(plugin).onTargetsAvailable(eq(listOf(
+ targets[0],
+ targets[3],
+ targets[5]
+ )))
+ }
+
+ @Test
+ fun testSettingsAreReloaded() {
+ // GIVEN a connected session where the privacy settings later flip to false
+ connectSession()
+ setAllowPrivateNotifications(userHandlePrimary, false)
+ setAllowPrivateNotifications(userHandleManaged, false)
+ settingsObserver.onChange(true, fakePrivateLockscreenSettingUri)
+
+ // WHEN we receive a new list of targets
+ val targets = listOf(
+ makeTarget(1, userHandlePrimary, isSensitive = true),
+ makeTarget(2, userHandleManaged, isSensitive = true),
+ makeTarget(4, userHandlePrimary, isSensitive = true)
+ )
+ sessionListener.onTargetsAvailable(targets)
+
+ // THEN we filter based on the new settings values
+ verify(plugin).onTargetsAvailable(emptyList())
+ }
+
+ @Test
+ fun testRecognizeSwitchToSecondaryUser() {
+ // GIVEN an inactive secondary user that doesn't allow sensitive content
+ setAllowPrivateNotifications(userHandleSecondary, false)
+ connectSession()
+
+ // WHEN the secondary user becomes the active user
+ setActiveUser(userHandleSecondary)
+ userListener.onUserChanged(userHandleSecondary.identifier, context)
+
+ // WHEN we receive a new list of targets
+ val targets = listOf(
+ makeTarget(0, userHandlePrimary),
+ makeTarget(1, userHandleSecondary),
+ makeTarget(2, userHandleSecondary, isSensitive = true),
+ makeTarget(3, userHandleManaged),
+ makeTarget(4, userHandleSecondary),
+ makeTarget(5, userHandleManaged),
+ makeTarget(6, userHandlePrimary)
+ )
+ sessionListener.onTargetsAvailable(targets)
+
+ // THEN only non-sensitive content from the secondary user is shown
+ verify(plugin).onTargetsAvailable(listOf(
+ targets[1],
+ targets[4]
+ ))
+ }
+
+ @Test
+ fun testUnregisterListenersOnCleanup() {
+ // GIVEN a connected session
+ connectSession()
+
+ // WHEN we are told to cleanup
+ controller.disconnect()
+
+ // THEN we disconnect from the session and unregister any listeners
+ verify(smartspaceSession).removeOnTargetsAvailableListener(sessionListener)
+ verify(smartspaceSession).close()
+ verify(userTracker).removeCallback(userListener)
+ verify(contentResolver).unregisterContentObserver(settingsObserver)
+ verify(configurationController).removeCallback(configChangeListener)
+ verify(statusBarStateController).removeCallback(statusBarStateListener)
+ }
+
+ @Test
+ fun testBuildViewIsIdempotent() {
+ // GIVEN a connected session
+ connectSession()
+ clearInvocations(plugin)
+
+ // WHEN we disconnect and then reconnect
+ controller.disconnect()
+ controller.buildAndConnectView(fakeParent)
+
+ // THEN the view is not rebuilt
+ verify(plugin, never()).getView(any())
+ assertEquals(fakeSmartspaceView, controller.view)
+ }
+
+ @Test
+ fun testDoubleConnectIsIgnored() {
+ // GIVEN a connected session
+ connectSession()
+ clearInvocations(smartspaceManager)
+ clearInvocations(plugin)
+
+ // WHEN we're asked to connect a second time
+ controller.buildAndConnectView(fakeParent)
+
+ // THEN the existing view and session are reused
+ verify(smartspaceManager, never()).createSmartspaceSession(any())
+ verify(plugin, never()).getView(any())
+ assertEquals(fakeSmartspaceView, controller.view)
+ }
+
+ private fun connectSession(): View {
+ val view = controller.buildAndConnectView(fakeParent)
+
+ verify(smartspaceSession)
+ .addOnTargetsAvailableListener(any(), capture(sessionListenerCaptor))
+ sessionListener = sessionListenerCaptor.value
+
+ verify(userTracker).addCallback(capture(userTrackerCaptor), any())
+ userListener = userTrackerCaptor.value
+
+ verify(contentResolver).registerContentObserver(
+ eq(fakePrivateLockscreenSettingUri),
+ eq(true),
+ capture(settingsObserverCaptor),
+ eq(UserHandle.USER_ALL))
+ settingsObserver = settingsObserverCaptor.value
+
+ verify(configurationController).addCallback(configChangeListenerCaptor.capture())
+ configChangeListener = configChangeListenerCaptor.value
+
+ verify(statusBarStateController).addCallback(statusBarStateListenerCaptor.capture())
+ statusBarStateListener = statusBarStateListenerCaptor.value
+
+ verify(smartspaceSession).requestSmartspaceUpdate()
+ clearInvocations(smartspaceSession)
+
+ verify(fakeSmartspaceView).setPrimaryTextColor(anyInt())
+ verify(fakeSmartspaceView).setDozeAmount(0.5f)
+ clearInvocations(fakeSmartspaceView)
+
+ return view
+ }
+
+ private fun setActiveUser(userHandle: UserHandle) {
+ `when`(userTracker.userId).thenReturn(userHandle.identifier)
+ `when`(userTracker.userHandle).thenReturn(userHandle)
+ }
+
+ private fun mockUserInfo(userHandle: UserHandle, isManagedProfile: Boolean): UserInfo {
+ val userInfo = mock(UserInfo::class.java)
+ `when`(userInfo.userHandle).thenReturn(userHandle)
+ `when`(userInfo.isManagedProfile).thenReturn(isManagedProfile)
+ return userInfo
+ }
+
+ fun makeTarget(
+ id: Int,
+ userHandle: UserHandle,
+ isSensitive: Boolean = false
+ ): SmartspaceTarget {
+ return SmartspaceTarget.Builder(
+ "target$id",
+ ComponentName("testpackage", "testclass$id"),
+ userHandle)
+ .setSensitive(isSensitive)
+ .build()
+ }
+
+ private fun setAllowPrivateNotifications(user: UserHandle, value: Boolean) {
+ `when`(secureSettings.getIntForUser(
+ eq(PRIVATE_LOCKSCREEN_SETTING),
+ anyInt(),
+ eq(user.identifier))
+ ).thenReturn(if (value) 1 else 0)
+ }
+
+ private val fakeSmartspaceView = spy(object : View(context), SmartspaceView {
+ override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {
+ }
+
+ override fun setPrimaryTextColor(color: Int) {
+ }
+
+ override fun setDozeAmount(amount: Float) {
+ }
+
+ override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {
+ }
+
+ override fun setFalsingManager(falsingManager: FalsingManager?) {
+ }
+
+ override fun setDnd(image: Drawable?, description: String?) {
+ }
+
+ override fun setNextAlarm(image: Drawable?, description: String?) {
+ }
+ })
+}
+
+private const val PRIVATE_LOCKSCREEN_SETTING =
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 896e330..9a7ab28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -29,6 +29,7 @@
import android.view.LayoutInflater
import android.widget.LinearLayout
import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.ActivityStarter
@@ -70,6 +71,7 @@
private val clock = FakeSystemClock()
private val mainExecutor = FakeExecutor(clock)
+ private val uiEventLoggerFake = UiEventLoggerFake()
private lateinit var controller: OngoingCallController
private lateinit var notifCollectionListener: NotifCollectionListener
@@ -99,7 +101,8 @@
clock,
mockActivityStarter,
mainExecutor,
- mockIActivityManager)
+ mockIActivityManager,
+ OngoingCallLogger(uiEventLoggerFake))
controller.init()
controller.addCallback(mockOngoingCallListener)
controller.setChipView(chipView)
@@ -256,6 +259,28 @@
.onOngoingCallStateChanged(anyBoolean())
}
+ @Test
+ fun chipClicked_clickEventLogged() {
+ notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+ chipView.performClick()
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_CLICKED.id)
+ }
+
+ @Test
+ fun notifyChipVisibilityChanged_visibleEventLogged() {
+ controller.notifyChipVisibilityChanged(true)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_VISIBLE.id)
+ }
+ // Other tests for notifyChipVisibilityChanged are in [OngoingCallLogger], since
+ // [OngoingCallController.notifyChipVisibilityChanged] just delegates to that class.
+
private fun createOngoingCallNotifEntry(): NotificationEntry {
val notificationEntryBuilder = NotificationEntryBuilder()
notificationEntryBuilder.modifyNotification(context).style = ongoingCallStyle
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
new file mode 100644
index 0000000..ecec124
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.ongoingcall
+
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+@SmallTest
+class OngoingCallLoggerTest : SysuiTestCase() {
+ private val uiEventLoggerFake = UiEventLoggerFake()
+ private val ongoingCallLogger = OngoingCallLogger(uiEventLoggerFake)
+
+ @Test
+ fun logChipClicked_clickEventLogged() {
+ ongoingCallLogger.logChipClicked()
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_CLICKED.id)
+ }
+
+ @Test
+ fun logChipVisibilityChanged_changeFromInvisibleToVisible_visibleEventLogged() {
+ ongoingCallLogger.logChipVisibilityChanged(false)
+ ongoingCallLogger.logChipVisibilityChanged(true)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_VISIBLE.id)
+ }
+
+ @Test
+ fun logChipVisibilityChanged_changeFromVisibleToInvisible_eventNotLogged() {
+ // Setting the chip to visible here will trigger a log
+ ongoingCallLogger.logChipVisibilityChanged(true)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+
+ ongoingCallLogger.logChipVisibilityChanged(false)
+
+ // Expect that there were no new logs
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ }
+
+ @Test
+ fun logChipVisibilityChanged_visibleThenVisibleAgain_eventNotLogged() {
+ // Setting the chip to visible here will trigger a log
+ ongoingCallLogger.logChipVisibilityChanged(true)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+
+ ongoingCallLogger.logChipVisibilityChanged(true)
+
+ // Expect that there were no new logs
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index 0e4b053..d72f432 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -25,12 +26,16 @@
import static org.mockito.Mockito.doReturn;
import android.app.Notification;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -49,6 +54,7 @@
private AccessibilityManagerWrapper mAccessibilityMgr;
private HeadsUpManager mHeadsUpManager;
private boolean mLivesPastNormalTime;
+ private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
private final class TestableHeadsUpManager extends HeadsUpManager {
TestableHeadsUpManager(Context context) {
@@ -65,6 +71,7 @@
@Before
public void setUp() {
mAccessibilityMgr = mDependency.injectMockDependency(AccessibilityManagerWrapper.class);
+ mDependency.injectTestDependency(UiEventLogger.class, mUiEventLoggerFake);
mHeadsUpManager = new TestableHeadsUpManager(mContext);
super.setUp();
@@ -108,5 +115,28 @@
assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0);
assertThat(activeRemoteInput.compareTo(ongoingCall)).isGreaterThan(0);
}
-}
+ @Test
+ public void testPinEntry_logsPeek() {
+ // Needs full screen intent in order to be pinned
+ final PendingIntent fullScreenIntent = PendingIntent.getActivity(mContext, 0,
+ new Intent(), PendingIntent.FLAG_MUTABLE);
+
+ HeadsUpManager.HeadsUpEntry entryToPin = mHeadsUpManager.new HeadsUpEntry();
+ entryToPin.setEntry(new NotificationEntryBuilder()
+ .setSbn(createNewSbn(0,
+ new Notification.Builder(mContext, "")
+ .setFullScreenIntent(fullScreenIntent, true)))
+ .build());
+ // Note: the standard way to show a notification would be calling showNotification rather
+ // than onAlertEntryAdded. However, in practice showNotification in effect adds
+ // the notification and then updates it; in order to not log twice, the entry needs
+ // to have a functional ExpandableNotificationRow that can keep track of whether it's
+ // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
+ mHeadsUpManager.onAlertEntryAdded(entryToPin);
+
+ assertEquals(1, mUiEventLoggerFake.numLogs());
+ assertEquals(HeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(),
+ mUiEventLoggerFake.eventId(0));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 2d9d715..9f1dad8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -185,7 +185,8 @@
Color.valueOf(Color.BLUE), null);
String jsonString =
- "{\"android.theme.customization.system_palette\":\"override.package.name\"}";
+ "{\"android.theme.customization.system_palette\":\"override.package.name\","
+ + "\"android.theme.customization.color_source\":\"preset\"}";
when(mSecureSettings.getStringForUser(
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
.thenReturn(jsonString);
@@ -203,6 +204,32 @@
}
@Test
+ public void onWallpaperColorsChanged_resetThemeIfNotPreset() {
+ // Should ask for a new theme when wallpaper colors change
+ WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
+ Color.valueOf(Color.BLUE), null);
+
+ String jsonString =
+ "{\"android.theme.customization.system_palette\":\"override.package.name\","
+ + "\"android.theme.customization.color_source\":\"home_wallpaper\"}";
+ when(mSecureSettings.getStringForUser(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+ .thenReturn(jsonString);
+
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+
+ ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
+ verify(mSecureSettings).putString(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture());
+
+ assertThat(updatedSetting.getValue().contains("android.theme.customization.system_palette"))
+ .isFalse();
+
+ verify(mThemeOverlayApplier)
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ }
+
+ @Test
public void onProfileAdded_setsTheme() {
mBroadcastReceiver.getValue().onReceive(null,
new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java
index ce71ac8..570e1d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java
@@ -16,6 +16,7 @@
package com.android.systemui.util.concurrency;
+import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.Executor;
@@ -25,11 +26,21 @@
*/
public class FakeThreadFactory implements ThreadFactory {
private final FakeExecutor mFakeExecutor;
+ private Handler mHandler;
public FakeThreadFactory(FakeExecutor fakeExecutor) {
mFakeExecutor = fakeExecutor;
}
+ public void setHandler(Handler handler) {
+ mHandler = handler;
+ }
+
+ @Override
+ public Handler builderHandlerOnNewThread(String threadName) {
+ return mHandler;
+ }
+
@Override
public Executor buildExecutorOnNewThread(String threadName) {
return mFakeExecutor;
@@ -41,6 +52,11 @@
}
@Override
+ public DelayableExecutor buildDelayableExecutorOnHandler(Handler handler) {
+ return mFakeExecutor;
+ }
+
+ @Override
public DelayableExecutor buildDelayableExecutorOnLooper(Looper looper) {
return mFakeExecutor;
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ae14e37..e4f2203 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -9134,7 +9134,8 @@
}
private NetworkCapabilities getNetworkCapabilitiesWithoutUids(@NonNull NetworkCapabilities nc) {
- final NetworkCapabilities sanitized = new NetworkCapabilities(nc);
+ final NetworkCapabilities sanitized = new NetworkCapabilities(nc,
+ NetworkCapabilities.REDACT_ALL);
sanitized.setUids(null);
sanitized.setAdministratorUids(new int[0]);
sanitized.setOwnerUid(Process.INVALID_UID);
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index db36e62..c3543e7 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -361,10 +361,13 @@
try {
executeRescueLevelInternal(context, level, failedPackage);
EventLogTags.writeRescueSuccess(level);
- logCriticalInfo(Log.DEBUG,
- "Finished rescue level " + levelToString(level));
+ String successMsg = "Finished rescue level " + levelToString(level);
+ if (!TextUtils.isEmpty(failedPackage)) {
+ successMsg += " for package " + failedPackage;
+ }
+ logCriticalInfo(Log.DEBUG, successMsg);
} catch (Throwable t) {
- logRescueException(level, t);
+ logRescueException(level, failedPackage, t);
}
}
@@ -427,7 +430,7 @@
pm.reboot(TAG);
}
} catch (Throwable t) {
- logRescueException(level, t);
+ logRescueException(level, failedPackage, t);
}
};
thread = new Thread(runnable);
@@ -441,7 +444,7 @@
try {
RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
} catch (Throwable t) {
- logRescueException(level, t);
+ logRescueException(level, failedPackage, t);
}
}
};
@@ -455,11 +458,15 @@
}
}
- private static void logRescueException(int level, Throwable t) {
+ private static void logRescueException(int level, @Nullable String failedPackageName,
+ Throwable t) {
final String msg = ExceptionUtils.getCompleteMessage(t);
EventLogTags.writeRescueFailure(level, msg);
- logCriticalInfo(Log.ERROR,
- "Failed rescue level " + levelToString(level) + ": " + msg);
+ String failureMsg = "Failed rescue level " + levelToString(level);
+ if (!TextUtils.isEmpty(failedPackageName)) {
+ failureMsg += " for package " + failedPackageName;
+ }
+ logCriticalInfo(Log.ERROR, failureMsg + ": " + msg);
}
private static int mapRescueLevelToUserImpact(int rescueLevel) {
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index a05eb68..ca59ce3 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -20,12 +20,17 @@
import static android.app.ActivityManager.RunningServiceInfo;
import static android.app.ActivityManager.RunningTaskInfo;
import static android.app.ActivityManager.getCurrentUser;
+import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_CAMERA;
+import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
+import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD;
import static android.content.Intent.EXTRA_PACKAGE_NAME;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.hardware.SensorPrivacyManager.EXTRA_ALL_SENSORS;
import static android.hardware.SensorPrivacyManager.EXTRA_SENSOR;
import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
@@ -60,6 +65,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -141,6 +147,7 @@
private static final int VER0_INDIVIDUAL_ENABLED = 1;
private static final int VER1_ENABLED = 0;
private static final int VER1_INDIVIDUAL_ENABLED = 1;
+ public static final int REMINDER_DIALOG_DELAY_MILLIS = 500;
private final Context mContext;
private final SensorPrivacyServiceImpl mSensorPrivacyServiceImpl;
@@ -206,6 +213,36 @@
private ArrayMap<Pair<String, UserHandle>, ArrayList<IBinder>> mSuppressReminders =
new ArrayMap<>();
+ private final ArrayMap<SensorUseReminderDialogInfo, ArraySet<Integer>>
+ mQueuedSensorUseReminderDialogs = new ArrayMap<>();
+
+ private class SensorUseReminderDialogInfo {
+ private int mTaskId;
+ private UserHandle mUser;
+ private String mPackageName;
+
+ SensorUseReminderDialogInfo(int taskId, UserHandle user, String packageName) {
+ mTaskId = taskId;
+ mUser = user;
+ mPackageName = packageName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof SensorUseReminderDialogInfo)) return false;
+ SensorUseReminderDialogInfo that = (SensorUseReminderDialogInfo) o;
+ return mTaskId == that.mTaskId
+ && Objects.equals(mUser, that.mUser)
+ && Objects.equals(mPackageName, that.mPackageName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mTaskId, mUser, mPackageName);
+ }
+ }
+
SensorPrivacyServiceImpl() {
mHandler = new SensorPrivacyHandler(FgThread.get().getLooper(), mContext);
File sensorPrivacyFile = new File(Environment.getDataSystemDirectory(),
@@ -228,7 +265,8 @@
}
}
- int[] micAndCameraOps = new int[]{OP_RECORD_AUDIO, OP_CAMERA};
+ int[] micAndCameraOps = new int[]{OP_RECORD_AUDIO, OP_PHONE_CALL_MICROPHONE,
+ OP_CAMERA, OP_PHONE_CALL_CAMERA};
mAppOpsManager.startWatchingNoted(micAndCameraOps, this);
mAppOpsManager.startWatchingStarted(micAndCameraOps, this);
@@ -254,15 +292,29 @@
public void onOpNoted(int code, int uid, String packageName,
String attributionTag, @AppOpsManager.OpFlags int flags,
@AppOpsManager.Mode int result) {
- if (result != MODE_IGNORED || (flags & AppOpsManager.OP_FLAGS_ALL_TRUSTED) == 0) {
+ if ((flags & AppOpsManager.OP_FLAGS_ALL_TRUSTED) == 0) {
return;
}
int sensor;
- if (code == OP_RECORD_AUDIO) {
- sensor = MICROPHONE;
+ if (result == MODE_IGNORED) {
+ if (code == OP_RECORD_AUDIO) {
+ sensor = MICROPHONE;
+ } else if (code == OP_CAMERA) {
+ sensor = CAMERA;
+ } else {
+ return;
+ }
+ } else if (result == MODE_ALLOWED) {
+ if (code == OP_PHONE_CALL_MICROPHONE) {
+ sensor = MICROPHONE;
+ } else if (code == OP_PHONE_CALL_CAMERA) {
+ sensor = CAMERA;
+ } else {
+ return;
+ }
} else {
- sensor = CAMERA;
+ return;
}
long token = Binder.clearCallingIdentity();
@@ -294,6 +346,11 @@
}
}
+ if (uid == Process.SYSTEM_UID) {
+ enqueueSensorUseReminderDialogAsync(-1, user, packageName, sensor);
+ return;
+ }
+
// TODO: Handle reminders with multiple sensors
// - If we have a likely activity that triggered the sensor use overlay a dialog over
@@ -312,7 +369,7 @@
if (task.isVisible && task.topActivity.getPackageName().equals(packageName)) {
if (task.isFocused) {
// There is the one focused activity
- showSensorUseReminderDialog(task.taskId, user, packageName, sensor);
+ enqueueSensorUseReminderDialogAsync(task.taskId, user, packageName, sensor);
return;
}
@@ -323,7 +380,7 @@
// TODO: Test this case
// There is one or more non-focused activity
if (tasksOfPackageUsingSensor.size() == 1) {
- showSensorUseReminderDialog(tasksOfPackageUsingSensor.get(0).taskId, user,
+ enqueueSensorUseReminderDialogAsync(tasksOfPackageUsingSensor.get(0).taskId, user,
packageName, sensor);
return;
} else if (tasksOfPackageUsingSensor.size() > 1) {
@@ -360,21 +417,60 @@
* @param packageName The name of the package using the sensor.
* @param sensor The sensor that is being used.
*/
- private void showSensorUseReminderDialog(int taskId, @NonNull UserHandle user,
+ private void enqueueSensorUseReminderDialogAsync(int taskId, @NonNull UserHandle user,
@NonNull String packageName, int sensor) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ this:: enqueueSensorUseReminderDialog, taskId, user, packageName, sensor));
+ }
+
+ private void enqueueSensorUseReminderDialog(int taskId, @NonNull UserHandle user,
+ @NonNull String packageName, int sensor) {
+ SensorUseReminderDialogInfo info =
+ new SensorUseReminderDialogInfo(taskId, user, packageName);
+ if (!mQueuedSensorUseReminderDialogs.containsKey(info)) {
+ ArraySet<Integer> sensors = new ArraySet<Integer>();
+ sensors.add(sensor);
+ mQueuedSensorUseReminderDialogs.put(info, sensors);
+ mHandler.sendMessageDelayed(
+ PooledLambda.obtainMessage(this::showSensorUserReminderDialog, info),
+ REMINDER_DIALOG_DELAY_MILLIS);
+ return;
+ }
+ ArraySet<Integer> sensors = mQueuedSensorUseReminderDialogs.get(info);
+ sensors.add(sensor);
+ }
+
+ private void showSensorUserReminderDialog(@NonNull SensorUseReminderDialogInfo info) {
+ ArraySet<Integer> sensors = mQueuedSensorUseReminderDialogs.get(info);
+ mQueuedSensorUseReminderDialogs.remove(info);
+ if (sensors == null) {
+ Log.e(TAG, "Unable to show sensor use dialog because sensor set is null."
+ + " Was the dialog queue modified from outside the handler thread?");
+ return;
+ }
Intent dialogIntent = new Intent();
dialogIntent.setComponent(ComponentName.unflattenFromString(
mContext.getResources().getString(
R.string.config_sensorUseStartedActivity)));
ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchTaskId(taskId);
+ options.setLaunchTaskId(info.mTaskId);
options.setTaskOverlay(true, true);
- dialogIntent.putExtra(EXTRA_PACKAGE_NAME, packageName);
- dialogIntent.putExtra(EXTRA_SENSOR, sensor);
+ dialogIntent.addFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- mContext.startActivityAsUser(dialogIntent, options.toBundle(), user);
+ dialogIntent.putExtra(EXTRA_PACKAGE_NAME, info.mPackageName);
+ if (sensors.size() == 1) {
+ dialogIntent.putExtra(EXTRA_SENSOR, sensors.valueAt(0));
+ } else if (sensors.size() == 2) {
+ dialogIntent.putExtra(EXTRA_ALL_SENSORS, true);
+ } else {
+ // Currently the only cases can be 1 or two
+ Log.e(TAG, "Attempted to show sensor use dialog for " + sensors.size()
+ + " sensors");
+ return;
+ }
+ mContext.startActivityAsUser(dialogIntent, options.toBundle(), info.mUser);
}
/**
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 2bb9084..e7e3ce9 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -167,7 +167,6 @@
@NonNull private final VcnNetworkProvider mNetworkProvider;
@NonNull private final TelephonySubscriptionTrackerCallback mTelephonySubscriptionTrackerCb;
@NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker;
- @NonNull private final VcnContext mVcnContext;
@NonNull private final BroadcastReceiver mPkgChangeReceiver;
@NonNull
@@ -212,7 +211,6 @@
mContext, mLooper, mTelephonySubscriptionTrackerCb);
mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE);
- mVcnContext = mDeps.newVcnContext(mContext, mLooper, mNetworkProvider);
mPkgChangeReceiver = new BroadcastReceiver() {
@Override
@@ -336,8 +334,9 @@
public VcnContext newVcnContext(
@NonNull Context context,
@NonNull Looper looper,
- @NonNull VcnNetworkProvider vcnNetworkProvider) {
- return new VcnContext(context, looper, vcnNetworkProvider);
+ @NonNull VcnNetworkProvider vcnNetworkProvider,
+ boolean isInTestMode) {
+ return new VcnContext(context, looper, vcnNetworkProvider, isInTestMode);
}
/** Creates a new Vcn instance using the provided configuration */
@@ -419,6 +418,14 @@
"Carrier privilege required for subscription group to set VCN Config");
}
+ private void enforceManageTestNetworksForTestMode(@NonNull VcnConfig vcnConfig) {
+ if (vcnConfig.isTestModeProfile()) {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.MANAGE_TEST_NETWORKS,
+ "Test-mode require the MANAGE_TEST_NETWORKS permission");
+ }
+ }
+
private class VcnSubscriptionTrackerCallback implements TelephonySubscriptionTrackerCallback {
/**
* Handles subscription group changes, as notified by {@link TelephonySubscriptionTracker}
@@ -542,8 +549,11 @@
final VcnCallbackImpl vcnCallback = new VcnCallbackImpl(subscriptionGroup);
+ final VcnContext vcnContext =
+ mDeps.newVcnContext(
+ mContext, mLooper, mNetworkProvider, config.isTestModeProfile());
final Vcn newInstance =
- mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot, vcnCallback);
+ mDeps.newVcn(vcnContext, subscriptionGroup, config, mLastSnapshot, vcnCallback);
mVcns.put(subscriptionGroup, newInstance);
// Now that a new VCN has started, notify all registered listeners to refresh their
@@ -587,6 +597,7 @@
mContext.getSystemService(AppOpsManager.class)
.checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName());
+ enforceManageTestNetworksForTestMode(config);
enforceCallingUserAndCarrierPrivilege(subscriptionGroup, opPkgName);
Binder.withCleanCallingIdentity(() -> {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 83cff15..6661f88 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -14621,7 +14621,9 @@
String reason, @TempAllowListType int type, int callingUid) {
synchronized (mProcLock) {
// The temp allowlist type could change according to the reasonCode.
- type = mLocalDeviceIdleController.getTempAllowListType(reasonCode, type);
+ if (mLocalDeviceIdleController != null) {
+ type = mLocalDeviceIdleController.getTempAllowListType(reasonCode, type);
+ }
if (type == TEMPORARY_ALLOW_LIST_TYPE_NONE) {
return;
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 50515882..a23b5eb 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3294,7 +3294,7 @@
boolean shouldCollectMessage) {
RestrictionBypass bypass;
try {
- bypass = verifyAndGetBypass(uid, packageName, attributionTag);
+ bypass = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
} catch (SecurityException e) {
Slog.e(TAG, "noteOperation", e);
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
@@ -3786,7 +3786,7 @@
boolean shouldCollectMessage, boolean dryRun) {
RestrictionBypass bypass;
try {
- bypass = verifyAndGetBypass(uid, packageName, attributionTag);
+ bypass = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
} catch (SecurityException e) {
Slog.e(TAG, "startOperation", e);
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
@@ -4318,17 +4318,26 @@
}
/**
+ * @see verifyAndGetBypass(int, String, String, String)
+ */
+ private @Nullable RestrictionBypass verifyAndGetBypass(int uid, String packageName,
+ @Nullable String attributionTag) {
+ return verifyAndGetBypass(uid, packageName, attributionTag, null);
+ }
+
+ /**
* Verify that package belongs to uid and return the {@link RestrictionBypass bypass
* description} for the package.
*
* @param uid The uid the package belongs to
* @param packageName The package the might belong to the uid
* @param attributionTag attribution tag or {@code null} if no need to verify
+ * @param proxyPackageName The proxy package, from which the attribution tag is to be pulled
*
* @return {@code true} iff the package is privileged
*/
private @Nullable RestrictionBypass verifyAndGetBypass(int uid, String packageName,
- @Nullable String attributionTag) {
+ @Nullable String attributionTag, @Nullable String proxyPackageName) {
if (uid == Process.ROOT_UID) {
// For backwards compatibility, don't check package name for root UID.
return null;
@@ -4366,34 +4375,36 @@
final long ident = Binder.clearCallingIdentity();
try {
boolean isAttributionTagValid = false;
- AndroidPackage pkg = LocalServices.getService(PackageManagerInternal.class)
- .getPackage(packageName);
+ PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
+ AndroidPackage pkg = pmInt.getPackage(packageName);
if (pkg != null) {
- if (attributionTag == null) {
- isAttributionTagValid = true;
- } else {
- if (pkg.getAttributions() != null) {
- int numAttributions = pkg.getAttributions().size();
- for (int i = 0; i < numAttributions; i++) {
- if (pkg.getAttributions().get(i).tag.equals(attributionTag)) {
- isAttributionTagValid = true;
- }
- }
- }
- }
+ isAttributionTagValid = isAttributionInPackage(pkg, attributionTag);
pkgUid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
bypass = getBypassforPackage(pkg);
}
if (!isAttributionTagValid) {
- String msg = "attributionTag " + attributionTag + " not declared in"
- + " manifest of " + packageName;
+ AndroidPackage proxyPkg = proxyPackageName != null
+ ? pmInt.getPackage(proxyPackageName) : null;
+ boolean foundInProxy = isAttributionInPackage(proxyPkg, attributionTag);
+ String msg;
+ if (pkg != null && foundInProxy) {
+ msg = "attributionTag " + attributionTag + " declared in manifest of the proxy"
+ + " package " + proxyPackageName + ", this is not advised";
+ } else if (pkg != null) {
+ msg = "attributionTag " + attributionTag + " not declared in manifest of "
+ + packageName;
+ } else {
+ msg = "package " + packageName + " not found, can't check for "
+ + "attributionTag " + attributionTag;
+ }
+
try {
if (mPlatformCompat.isChangeEnabledByPackageName(
SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, packageName,
userId) && mPlatformCompat.isChangeEnabledByUid(
SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE,
- callingUid)) {
+ callingUid) && !foundInProxy) {
throw new SecurityException(msg);
} else {
Slog.e(TAG, msg);
@@ -4413,6 +4424,25 @@
return bypass;
}
+ private boolean isAttributionInPackage(@Nullable AndroidPackage pkg,
+ @Nullable String attributionTag) {
+ if (pkg == null) {
+ return false;
+ } else if (attributionTag == null) {
+ return true;
+ }
+ if (pkg.getAttributions() != null) {
+ int numAttributions = pkg.getAttributions().size();
+ for (int i = 0; i < numAttributions; i++) {
+ if (pkg.getAttributions().get(i).tag.equals(attributionTag)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
/**
* Get (and potentially create) ops.
*
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 96bb73f..8961a5a 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1034,6 +1034,11 @@
}
}
+ /*package*/ void clearAvrcpAbsoluteVolumeSupported() {
+ setAvrcpAbsoluteVolumeSupported(false);
+ mAudioService.setAvrcpAbsoluteVolumeSupported(false);
+ }
+
/*package*/ boolean getBluetoothA2dpEnabled() {
synchronized (mDeviceStateLock) {
return mBluetoothA2dpEnabled;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 18d04e9..5944a63 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1015,7 +1015,7 @@
}
// device to remove was visible by APM, update APM
- mDeviceBroker.setAvrcpAbsoluteVolumeSupported(false);
+ mDeviceBroker.clearAvrcpAbsoluteVolumeSupported();
final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", a2dpCodec);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 098ce7c..a3c5904 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -6535,6 +6535,10 @@
if (index == -1) {
continue;
}
+ if (mPublicStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED
+ && mCameraSoundForced) {
+ index = mIndexMax;
+ }
if (DEBUG_VOL) {
Log.v(TAG, "readSettings: found stored index " + getValidIndex(index)
+ " for group " + mAudioVolumeGroup.name() + ", device: " + name
@@ -7643,8 +7647,12 @@
// address is not used for now, but may be used when multiple a2dp devices are supported
sVolumeLogger.log(new AudioEventLogger.StringEvent("avrcpSupportsAbsoluteVolume addr="
+ address + " support=" + support));
- mAvrcpAbsVolSupported = support;
mDeviceBroker.setAvrcpAbsoluteVolumeSupported(support);
+ setAvrcpAbsoluteVolumeSupported(support);
+ }
+
+ /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean support) {
+ mAvrcpAbsVolSupported = support;
sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
mStreamStates[AudioSystem.STREAM_MUSIC], 0);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index dd3057e7..6a7d201 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -149,7 +149,7 @@
prop.supportsDetectInteraction, prop.halControlsPreview,
false /* resetLockoutRequiresChallenge */);
final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
- internalProp);
+ internalProp, lockoutResetDispatcher);
mSensors.put(sensorId, sensor);
Slog.d(getTag(), "Added: " + internalProp);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index f551930..1e1b532 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -80,11 +80,26 @@
}
void onLockoutCleared() {
- mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_NONE);
- mLockoutResetDispatcher.notifyLockoutResetCallbacks(getSensorId());
+ resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache,
+ mLockoutResetDispatcher);
mCallback.onClientFinished(this, true /* success */);
}
+ /**
+ * Reset the local lockout state and notify any listeners.
+ *
+ * This should only be called when the HAL sends a reset request directly to the
+ * framework (i.e. time based reset, etc.). When the HAL is responding to a
+ * resetLockout request from an instance of this client {@link #onLockoutCleared()} should
+ * be used instead.
+ */
+ static void resetLocalLockoutStateToNone(int sensorId, int userId,
+ @NonNull LockoutCache lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+ lockoutTracker.setLockoutModeForUser(userId, LockoutTracker.LOCKOUT_NONE);
+ lockoutResetDispatcher.notifyLockoutResetCallbacks(sensorId);
+ }
+
@Override
public int getProtoEnum() {
return BiometricsProto.CM_RESET_LOCKOUT;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 724531e..0e6a0f7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -57,6 +57,7 @@
import com.android.server.biometrics.sensors.Interruptable;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.RemovalConsumer;
import com.android.server.biometrics.sensors.StartUserClient;
import com.android.server.biometrics.sensors.StopUserClient;
@@ -124,10 +125,16 @@
private final int mSensorId;
private final int mUserId;
@NonNull
+ private final LockoutCache mLockoutCache;
+ @NonNull
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+ @NonNull
private final Callback mCallback;
HalSessionCallback(@NonNull Context context, @NonNull Handler handler, @NonNull String tag,
@NonNull UserAwareBiometricScheduler scheduler, int sensorId, int userId,
+ @NonNull LockoutCache lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull Callback callback) {
mContext = context;
mHandler = handler;
@@ -135,6 +142,8 @@
mScheduler = scheduler;
mSensorId = sensorId;
mUserId = userId;
+ mLockoutCache = lockoutTracker;
+ mLockoutResetDispatcher = lockoutResetDispatcher;
mCallback = callback;
}
@@ -327,13 +336,15 @@
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof FaceResetLockoutClient)) {
- Slog.e(mTag, "onLockoutCleared for non-resetLockout client: "
- + Utils.getClientName(client));
- return;
+ Slog.d(mTag, "onLockoutCleared outside of resetLockout by HAL");
+ FaceResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId,
+ mLockoutCache, mLockoutResetDispatcher);
+ } else {
+ Slog.d(mTag, "onLockoutCleared after resetLockout");
+ final FaceResetLockoutClient resetLockoutClient =
+ (FaceResetLockoutClient) client;
+ resetLockoutClient.onLockoutCleared();
}
-
- final FaceResetLockoutClient resetLockoutClient = (FaceResetLockoutClient) client;
- resetLockoutClient.onLockoutCleared();
});
}
@@ -465,7 +476,8 @@
}
Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
- @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties) {
+ @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
mTag = tag;
mProvider = provider;
mContext = context;
@@ -493,7 +505,8 @@
final int sensorId = mSensorProperties.sensorId;
final HalSessionCallback resultController = new HalSessionCallback(mContext,
- mHandler, mTag, mScheduler, sensorId, newUserId, callback);
+ mHandler, mTag, mScheduler, sensorId, newUserId, mLockoutCache,
+ lockoutResetDispatcher, callback);
final StartUserClient.UserStartedCallback<ISession> userStartedCallback =
(userIdStarted, newSession) -> {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 20d6ee2..e5fafcd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -159,7 +159,7 @@
prop.sensorLocations[0].sensorLocationY,
prop.sensorLocations[0].sensorRadius);
final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
- internalProp, gestureAvailabilityDispatcher);
+ internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher);
mSensors.put(sensorId, sensor);
Slog.d(getTag(), "Added: " + internalProp);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index bab9506..878ef46 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -80,11 +80,26 @@
}
void onLockoutCleared() {
- mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_NONE);
- mLockoutResetDispatcher.notifyLockoutResetCallbacks(getSensorId());
+ resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache,
+ mLockoutResetDispatcher);
mCallback.onClientFinished(this, true /* success */);
}
+ /**
+ * Reset the local lockout state and notify any listeners.
+ *
+ * This should only be called when the HAL sends a reset request directly to the
+ * framework (i.e. time based reset, etc.). When the HAL is responding to a
+ * resetLockout request from an instance of this client {@link #onLockoutCleared()} should
+ * be used instead.
+ */
+ static void resetLocalLockoutStateToNone(int sensorId, int userId,
+ @NonNull LockoutCache lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+ lockoutTracker.setLockoutModeForUser(userId, LockoutTracker.LOCKOUT_NONE);
+ lockoutResetDispatcher.notifyLockoutResetCallbacks(sensorId);
+ }
+
@Override
public int getProtoEnum() {
return BiometricsProto.CM_RESET_LOCKOUT;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index cf915ad..9884a78 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -54,6 +54,7 @@
import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.RemovalConsumer;
import com.android.server.biometrics.sensors.StartUserClient;
import com.android.server.biometrics.sensors.StopUserClient;
@@ -116,16 +117,27 @@
void onHardwareUnavailable();
}
- @NonNull private final Context mContext;
- @NonNull private final Handler mHandler;
- @NonNull private final String mTag;
- @NonNull private final UserAwareBiometricScheduler mScheduler;
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final Handler mHandler;
+ @NonNull
+ private final String mTag;
+ @NonNull
+ private final UserAwareBiometricScheduler mScheduler;
private final int mSensorId;
private final int mUserId;
- @NonNull private final Callback mCallback;
+ @NonNull
+ private final LockoutCache mLockoutCache;
+ @NonNull
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+ @NonNull
+ private final Callback mCallback;
HalSessionCallback(@NonNull Context context, @NonNull Handler handler, @NonNull String tag,
@NonNull UserAwareBiometricScheduler scheduler, int sensorId, int userId,
+ @NonNull LockoutCache lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull Callback callback) {
mContext = context;
mHandler = handler;
@@ -133,6 +145,8 @@
mScheduler = scheduler;
mSensorId = sensorId;
mUserId = userId;
+ mLockoutCache = lockoutTracker;
+ mLockoutResetDispatcher = lockoutResetDispatcher;
mCallback = callback;
}
@@ -303,14 +317,15 @@
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof FingerprintResetLockoutClient)) {
- Slog.e(mTag, "onLockoutCleared for non-resetLockout client: "
- + Utils.getClientName(client));
- return;
+ Slog.d(mTag, "onLockoutCleared outside of resetLockout by HAL");
+ FingerprintResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId,
+ mLockoutCache, mLockoutResetDispatcher);
+ } else {
+ Slog.d(mTag, "onLockoutCleared after resetLockout");
+ final FingerprintResetLockoutClient resetLockoutClient =
+ (FingerprintResetLockoutClient) client;
+ resetLockoutClient.onLockoutCleared();
}
-
- final FingerprintResetLockoutClient resetLockoutClient =
- (FingerprintResetLockoutClient) client;
- resetLockoutClient.onLockoutCleared();
});
}
@@ -415,6 +430,7 @@
Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
@NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
mTag = tag;
mProvider = provider;
@@ -422,6 +438,7 @@
mToken = new Binder();
mHandler = handler;
mSensorProperties = sensorProperties;
+ mLockoutCache = new LockoutCache();
mScheduler = new UserAwareBiometricScheduler(tag, gestureAvailabilityDispatcher,
() -> mCurrentSession != null ? mCurrentSession.mUserId : UserHandle.USER_NULL,
new UserAwareBiometricScheduler.UserSwitchCallback() {
@@ -443,7 +460,8 @@
final int sensorId = mSensorProperties.sensorId;
final HalSessionCallback resultController = new HalSessionCallback(mContext,
- mHandler, mTag, mScheduler, sensorId, newUserId, callback);
+ mHandler, mTag, mScheduler, sensorId, newUserId, mLockoutCache,
+ lockoutResetDispatcher, callback);
final StartUserClient.UserStartedCallback<ISession> userStartedCallback =
(userIdStarted, newSession) -> {
@@ -466,7 +484,6 @@
resultController, userStartedCallback);
}
});
- mLockoutCache = new LockoutCache();
mAuthenticatorIds = new HashMap<>();
mLazySession = () -> mCurrentSession != null ? mCurrentSession.mSession : null;
}
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 28f208b..5886b1a 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -338,7 +338,8 @@
return currentPermission;
}
try {
- final PackageInfo app = mPackageManager.getPackageInfo(name, GET_PERMISSIONS);
+ final PackageInfo app = mPackageManager.getPackageInfo(name,
+ GET_PERMISSIONS | MATCH_ANY_USER);
final boolean isNetwork = hasNetworkPermission(app);
final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
if (isNetwork || hasRestrictedPermission) {
@@ -664,6 +665,7 @@
break;
case INetd.PERMISSION_UNINSTALLED:
uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i));
+ break;
default:
Log.e(TAG, "unknown permission type: " + permissions + "for uid: "
+ netdPermissionsAppIds.keyAt(i));
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 0f9e604..314955b 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -689,6 +689,8 @@
if (sensorDetails != null) {
mAmbientLightSensor.type = sensorDetails.getType();
mAmbientLightSensor.name = sensorDetails.getName();
+ } else {
+ loadAmbientLightSensorFromConfigXml();
}
}
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 609bd8b..0949ddd 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -20,6 +20,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.Typeface;
import android.graphics.fonts.Font;
import android.graphics.fonts.FontFamily;
@@ -32,6 +34,9 @@
import android.os.ShellCallback;
import android.system.ErrnoException;
import android.text.FontConfig;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AndroidException;
import android.util.IndentingPrintWriter;
@@ -196,10 +201,32 @@
}
@Override
- public void tryToCreateTypeface(File file) throws IOException {
+ public void tryToCreateTypeface(File file) throws Throwable {
Font font = new Font.Builder(file).build();
FontFamily family = new FontFamily.Builder(font).build();
- new Typeface.CustomFallbackBuilder(family).build();
+ Typeface typeface = new Typeface.CustomFallbackBuilder(family).build();
+
+ TextPaint p = new TextPaint();
+ p.setTextSize(24f);
+ p.setTypeface(typeface);
+
+ // Test string to try with the passed font.
+ // TODO: Good to extract from font file.
+ String testTextToDraw = "abcXYZ@- "
+ + "\uD83E\uDED6" // Emoji E13.0
+ + "\uD83C\uDDFA\uD83C\uDDF8" // Emoji Flags
+ + "\uD83D\uDC8F\uD83C\uDFFB" // Emoji Skin tone Sequence
+ // ZWJ Sequence
+ + "\uD83D\uDC68\uD83C\uDFFC\u200D\u2764\uFE0F\u200D\uD83D\uDC8B\u200D"
+ + "\uD83D\uDC68\uD83C\uDFFF";
+
+ int width = (int) Math.ceil(Layout.getDesiredWidth(testTextToDraw, p));
+ StaticLayout layout = StaticLayout.Builder.obtain(
+ testTextToDraw, 0, testTextToDraw.length(), p, width).build();
+ Bitmap bmp = Bitmap.createBitmap(
+ layout.getWidth(), layout.getHeight(), Bitmap.Config.ALPHA_8);
+ Canvas canvas = new Canvas(bmp);
+ layout.draw(canvas);
}
private static ByteBuffer mmap(File file) throws IOException {
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index e74dac5..981cc838 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -69,7 +69,7 @@
long getRevision(File file) throws IOException;
- void tryToCreateTypeface(File file) throws IOException;
+ void tryToCreateTypeface(File file) throws Throwable;
}
/** Interface to mock fs-verity in tests. */
@@ -378,10 +378,10 @@
// Try to create Typeface and treat as failure something goes wrong.
try {
mParser.tryToCreateTypeface(fontFileInfo.getFile());
- } catch (IOException e) {
+ } catch (Throwable t) {
throw new SystemFontException(
FontManager.RESULT_ERROR_INVALID_FONT_FILE,
- "Failed to create Typeface from file", e);
+ "Failed to create Typeface from file", t);
}
FontConfig fontConfig = getSystemFontConfig();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 10f6948f..dcd0eb8 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -136,18 +136,19 @@
if (!mService.isControlEnabled()) {
return;
}
- if (isActiveSource()) {
- mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(
- mAddress, mService.getPhysicalAddress()));
- }
boolean wasActiveSource = isActiveSource();
- // Invalidate the internal active source record when goes to standby
+ // Invalidate the internal active source record when going to standby
mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS,
"HdmiCecLocalDevicePlayback#onStandby()");
boolean mTvSendStandbyOnSleep = mService.getHdmiCecConfig().getIntValue(
HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
== HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED;
- if (initiatedByCec || !mTvSendStandbyOnSleep || !wasActiveSource) {
+ if (!wasActiveSource) {
+ return;
+ }
+ if (initiatedByCec || !mTvSendStandbyOnSleep) {
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(mAddress,
+ mService.getPhysicalAddress()));
return;
}
switch (standbyAction) {
@@ -167,6 +168,9 @@
Constants.ADDR_BROADCAST));
break;
case HdmiControlManager.POWER_CONTROL_MODE_NONE:
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildInactiveSource(mAddress,
+ mService.getPhysicalAddress()));
break;
}
break;
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 1c27c65..61107b2 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -313,6 +313,7 @@
private static native void nativeSetFocusedDisplay(long ptr, int displayId);
private static native boolean nativeTransferTouchFocus(long ptr,
IBinder fromChannelToken, IBinder toChannelToken, boolean isDragDrop);
+ private static native boolean nativeTransferTouch(long ptr, IBinder destChannelToken);
private static native void nativeSetPointerSpeed(long ptr, int speed);
private static native void nativeSetShowTouches(long ptr, boolean enabled);
private static native void nativeSetInteractive(long ptr, boolean interactive);
@@ -676,6 +677,19 @@
}
/**
+ * Transfer the current touch gesture to the provided window.
+ *
+ * @param destChannelToken The token of the window or input channel that should receive the
+ * gesture
+ * @return True if the transfer succeeded, false if there was no active touch gesture happening
+ */
+ public boolean transferTouch(IBinder destChannelToken) {
+ // TODO(b/162194035): Replace this with a SPY window
+ Objects.requireNonNull(destChannelToken, "destChannelToken must not be null.");
+ return nativeTransferTouch(mPtr, destChannelToken);
+ }
+
+ /**
* Creates an input channel that will receive all input from the input dispatcher.
* @param inputChannelName The input channel name.
* @param displayId Target display id.
diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java
index ec79483..ed00609 100644
--- a/services/core/java/com/android/server/pm/DumpState.java
+++ b/services/core/java/com/android/server/pm/DumpState.java
@@ -58,6 +58,7 @@
private boolean mTitlePrinted;
private boolean mFullPreferred;
private boolean mCheckIn;
+ private boolean mBrief;
private String mTargetPackageName;
@@ -128,4 +129,12 @@
public void setCheckIn(boolean checkIn) {
mCheckIn = checkIn;
}
+
+ public boolean isBrief() {
+ return mBrief;
+ }
+
+ public void setBrief(boolean brief) {
+ mBrief = brief;
+ }
}
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index 2015c78..34caaf5 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -30,6 +30,7 @@
import android.util.TypedXmlSerializer;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.utils.WatchedArrayMap;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -65,7 +66,7 @@
protected final LongSparseArray<ArraySet<Long>> mKeySetMapping;
- private final ArrayMap<String, PackageSetting> mPackages;
+ private final WatchedArrayMap<String, PackageSetting> mPackages;
private long lastIssuedKeySetId = 0;
@@ -114,7 +115,7 @@
}
}
- public KeySetManagerService(ArrayMap<String, PackageSetting> packages) {
+ public KeySetManagerService(WatchedArrayMap<String, PackageSetting> packages) {
mKeySets = new LongSparseArray<KeySetHandle>();
mPublicKeys = new LongSparseArray<PublicKeyHandle>();
mKeySetMapping = new LongSparseArray<ArraySet<Long>>();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 679042f..427bb2d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -404,6 +404,7 @@
import com.android.server.rollback.RollbackManagerInternal;
import com.android.server.storage.DeviceStorageMonitorInternal;
import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.utils.SnapshotCache;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.utils.Watchable;
import com.android.server.utils.Watched;
@@ -871,12 +872,17 @@
@Watched
@GuardedBy("mLock")
final WatchedArrayMap<String, AndroidPackage> mPackages = new WatchedArrayMap<>();
+ private final SnapshotCache<WatchedArrayMap<String, AndroidPackage>> mPackagesSnapshot =
+ new SnapshotCache.Auto(mPackages, mPackages, "PackageManagerService.mPackages");
// Keys are isolated uids and values are the uid of the application
// that created the isolated process.
@Watched
@GuardedBy("mLock")
final WatchedSparseIntArray mIsolatedOwners = new WatchedSparseIntArray();
+ private final SnapshotCache<WatchedSparseIntArray> mIsolatedOwnersSnapshot =
+ new SnapshotCache.Auto(mIsolatedOwners, mIsolatedOwners,
+ "PackageManagerService.mIsolatedOwners");
/**
* Tracks new system packages [received in an OTA] that we expect to
@@ -1309,14 +1315,17 @@
// Avoid invalidation-thrashing by preventing cache invalidations from causing property
// writes if the cache isn't enabled yet. We re-enable writes later when we're
// done initializing.
- sSnapshotCorked = true;
+ sSnapshotCorked.incrementAndGet();
PackageManager.corkPackageInfoCache();
}
@Override
public void enablePackageCaches() {
// Uncork cache invalidations and allow clients to cache package information.
- sSnapshotCorked = false;
+ int corking = sSnapshotCorked.decrementAndGet();
+ if (TRACE_SNAPSHOTS && corking == 0) {
+ Log.i(TAG, "snapshot: corking returns to 0");
+ }
PackageManager.uncorkPackageInfoCache();
}
}
@@ -1395,14 +1404,27 @@
@Watched
final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
mSharedLibraries = new WatchedArrayMap<>();
+ private final SnapshotCache<WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>>
+ mSharedLibrariesSnapshot =
+ new SnapshotCache.Auto<>(mSharedLibraries, mSharedLibraries,
+ "PackageManagerService.mSharedLibraries");
@Watched
final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
mStaticLibsByDeclaringPackage = new WatchedArrayMap<>();
+ private final SnapshotCache<WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>>
+ mStaticLibsByDeclaringPackageSnapshot =
+ new SnapshotCache.Auto<>(mSharedLibraries, mSharedLibraries,
+ "PackageManagerService.mSharedLibraries");
// Mapping from instrumentation class names to info about them.
@Watched
final WatchedArrayMap<ComponentName, ParsedInstrumentation> mInstrumentation =
new WatchedArrayMap<>();
+ private final SnapshotCache<WatchedArrayMap<ComponentName, ParsedInstrumentation>>
+ mInstrumentationSnapshot =
+ new SnapshotCache.Auto<>(mInstrumentation, mInstrumentation,
+ "PackageManagerService.mInstrumentation");
+
// Packages whose data we have transfered into another package, thus
// should no longer exist.
@@ -1588,6 +1610,7 @@
static final int INTEGRITY_VERIFICATION_COMPLETE = 25;
static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26;
static final int DOMAIN_VERIFICATION = 27;
+ static final int SNAPSHOT_UNCORK = 28;
static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
@@ -1834,11 +1857,11 @@
Snapshot(int type) {
if (type == Snapshot.SNAPPED) {
settings = mSettings.snapshot();
- isolatedOwners = mIsolatedOwners.snapshot();
- packages = mPackages.snapshot();
- sharedLibs = mSharedLibraries.snapshot();
- staticLibs = mStaticLibsByDeclaringPackage.snapshot();
- instrumentation = mInstrumentation.snapshot();
+ isolatedOwners = mIsolatedOwnersSnapshot.snapshot();
+ packages = mPackagesSnapshot.snapshot();
+ sharedLibs = mSharedLibrariesSnapshot.snapshot();
+ staticLibs = mStaticLibsByDeclaringPackageSnapshot.snapshot();
+ instrumentation = mInstrumentationSnapshot.snapshot();
resolveComponentName = mResolveComponentName.clone();
resolveActivity = new ActivityInfo(mResolveActivity);
instantAppInstallerActivity =
@@ -4874,12 +4897,16 @@
// A lock-free cache for frequently called functions.
private volatile Computer mSnapshotComputer;
// If true, the snapshot is invalid (stale). The attribute is static since it may be
- // set from outside classes.
- private static volatile boolean sSnapshotInvalid = true;
+ // set from outside classes. The attribute may be set to true anywhere, although it
+ // should only be set true while holding mLock. However, the attribute id guaranteed
+ // to be set false only while mLock and mSnapshotLock are both held.
+ private static AtomicBoolean sSnapshotInvalid = new AtomicBoolean(true);
+ // The package manager that is using snapshots.
+ private static PackageManagerService sSnapshotConsumer = null;
// If true, the snapshot is corked. Do not create a new snapshot but use the live
// computer. This throttles snapshot creation during periods of churn in Package
// Manager.
- private static volatile boolean sSnapshotCorked = false;
+ private static AtomicInteger sSnapshotCorked = new AtomicInteger(0);
/**
* This lock is used to make reads from {@link #sSnapshotInvalid} and
@@ -4897,7 +4924,10 @@
// The snapshot disable/enable switch. An image with the flag set true uses snapshots
// and an image with the flag set false does not use snapshots.
- private static final boolean SNAPSHOT_ENABLED = false;
+ private static final boolean SNAPSHOT_ENABLED = true;
+
+ // The default auto-cork delay for snapshots. This is 1s.
+ private static final long SNAPSHOT_AUTOCORK_DELAY_MS = TimeUnit.SECONDS.toMillis(1);
// The per-instance snapshot disable/enable flag. This is generally set to false in
// test instances and set to SNAPSHOT_ENABLED in operational instances.
@@ -4922,15 +4952,16 @@
// If the current thread holds mLock then it may have modified state but not
// yet invalidated the snapshot. Always give the thread the live computer.
return mLiveComputer;
+ } else if (sSnapshotCorked.get() > 0) {
+ // Snapshots are corked, which means new ones should not be built right now.
+ mSnapshotStatistics.corked();
+ return mLiveComputer;
}
synchronized (mSnapshotLock) {
+ // This synchronization block serializes access to the snapshot computer and
+ // to the code that samples mSnapshotInvalid.
Computer c = mSnapshotComputer;
- if (sSnapshotCorked && (c != null)) {
- // Snapshots are corked, which means new ones should not be built right now.
- c.use();
- return c;
- }
- if (sSnapshotInvalid || (c == null)) {
+ if (sSnapshotInvalid.getAndSet(false) || (c == null)) {
// The snapshot is invalid if it is marked as invalid or if it is null. If it
// is null, then it is currently being rebuilt by rebuildSnapshot().
synchronized (mLock) {
@@ -4938,9 +4969,7 @@
// invalidated as it is rebuilt. However, the snapshot is still
// self-consistent (the lock is being held) and is current as of the time
// this function is entered.
- if (sSnapshotInvalid) {
- rebuildSnapshot();
- }
+ rebuildSnapshot();
// Guaranteed to be non-null. mSnapshotComputer is only be set to null
// temporarily in rebuildSnapshot(), which is guarded by mLock(). Since
@@ -4958,12 +4987,11 @@
* Rebuild the cached computer. mSnapshotComputer is temporarily set to null to block other
* threads from using the invalid computer until it is rebuilt.
*/
- @GuardedBy("mLock")
+ @GuardedBy({ "mLock", "mSnapshotLock"})
private void rebuildSnapshot() {
final long now = SystemClock.currentTimeMicro();
final int hits = mSnapshotComputer == null ? -1 : mSnapshotComputer.getUsed();
mSnapshotComputer = null;
- sSnapshotInvalid = false;
final Snapshot args = new Snapshot(Snapshot.SNAPPED);
mSnapshotComputer = new ComputerEngine(args);
final long done = SystemClock.currentTimeMicro();
@@ -4972,6 +5000,30 @@
}
/**
+ * Create a new snapshot. Used for testing only. This does collect statistics or
+ * update the snapshot used by other actors. It does not alter the invalidation
+ * flag. This method takes the mLock internally.
+ */
+ private Computer createNewSnapshot() {
+ synchronized (mLock) {
+ final Snapshot args = new Snapshot(Snapshot.SNAPPED);
+ return new ComputerEngine(args);
+ }
+ }
+
+ /**
+ * Cork snapshots. This times out after the programmed delay.
+ */
+ private void corkSnapshots(int multiplier) {
+ int corking = sSnapshotCorked.getAndIncrement();
+ if (TRACE_SNAPSHOTS && corking == 0) {
+ Log.i(TAG, "snapshot: corking goes positive");
+ }
+ Message message = mHandler.obtainMessage(SNAPSHOT_UNCORK);
+ mHandler.sendMessageDelayed(message, SNAPSHOT_AUTOCORK_DELAY_MS * multiplier);
+ }
+
+ /**
* Create a live computer
*/
private ComputerLocked createLiveComputer() {
@@ -4986,9 +5038,9 @@
*/
public static void onChange(@Nullable Watchable what) {
if (TRACE_SNAPSHOTS) {
- Log.e(TAG, "snapshot: onChange(" + what + ")");
+ Log.i(TAG, "snapshot: onChange(" + what + ")");
}
- sSnapshotInvalid = true;
+ sSnapshotInvalid.set(true);
}
/**
@@ -5367,6 +5419,13 @@
mDomainVerificationManager.runMessage(messageCode, object);
break;
}
+ case SNAPSHOT_UNCORK: {
+ int corking = sSnapshotCorked.decrementAndGet();
+ if (TRACE_SNAPSHOTS && corking == 0) {
+ Log.e(TAG, "snapshot: corking goes to zero in message handler");
+ }
+ break;
+ }
}
}
}
@@ -6383,12 +6442,13 @@
// constructor, at which time the invalidation method updates it. The cache is
// corked initially to ensure a cached computer is not built until the end of the
// constructor.
- mSnapshotEnabled = SNAPSHOT_ENABLED;
- sSnapshotCorked = true;
- sSnapshotInvalid = true;
mSnapshotStatistics = new SnapshotStatistics();
+ sSnapshotConsumer = this;
+ sSnapshotCorked.set(1);
+ sSnapshotInvalid.set(true);
mLiveComputer = createLiveComputer();
mSnapshotComputer = null;
+ mSnapshotEnabled = SNAPSHOT_ENABLED;
registerObserver();
}
@@ -18521,7 +18581,7 @@
}
}
- @GuardedBy({"mInstallLock", "mLock"})
+ @GuardedBy("mInstallLock")
private void installPackagesTracedLI(List<InstallRequest> requests) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
@@ -24018,6 +24078,15 @@
dumpState.setDump(DumpState.DUMP_PER_UID_READ_TIMEOUTS);
} else if ("snapshot".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_SNAPSHOT_STATISTICS);
+ if (opti < args.length) {
+ if ("--full".equals(args[opti])) {
+ dumpState.setBrief(false);
+ opti++;
+ } else if ("--brief".equals(args[opti])) {
+ dumpState.setBrief(true);
+ opti++;
+ }
+ }
} else if ("write".equals(cmd)) {
synchronized (mLock) {
writeSettingsLPrTEMP();
@@ -24353,13 +24422,14 @@
pw.println(" Snapshots disabled");
} else {
int hits = 0;
+ int level = sSnapshotCorked.get();
synchronized (mSnapshotLock) {
if (mSnapshotComputer != null) {
hits = mSnapshotComputer.getUsed();
}
}
final long now = SystemClock.currentTimeMicro();
- mSnapshotStatistics.dump(pw, " ", now, hits, true);
+ mSnapshotStatistics.dump(pw, " ", now, hits, level, dumpState.isBrief());
}
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 1b8eee3..f5a13d5 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -351,6 +351,7 @@
private final PackageManagerTracedLock mLock;
+ @Watched(manual = true)
private final RuntimePermissionPersistence mRuntimePermissionsPersistence;
private final File mSettingsFilename;
@@ -364,19 +365,21 @@
/** Map from package name to settings */
@Watched
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- final WatchedArrayMap<String, PackageSetting> mPackages = new WatchedArrayMap<>();
+ final WatchedArrayMap<String, PackageSetting> mPackages;
+ private final SnapshotCache<WatchedArrayMap<String, PackageSetting>> mPackagesSnapshot;
/**
* List of packages that were involved in installing other packages, i.e. are listed
* in at least one app's InstallSource.
*/
@Watched
- private final WatchedArraySet<String> mInstallerPackages = new WatchedArraySet<>();
+ private final WatchedArraySet<String> mInstallerPackages;
+ private final SnapshotCache<WatchedArraySet<String>> mInstallerPackagesSnapshot;
/** Map from package name to appId and excluded userids */
@Watched
- private final WatchedArrayMap<String, KernelPackageState> mKernelMapping =
- new WatchedArrayMap<>();
+ private final WatchedArrayMap<String, KernelPackageState> mKernelMapping;
+ private final SnapshotCache<WatchedArrayMap<String, KernelPackageState>> mKernelMappingSnapshot;
// List of replaced system applications
@Watched
@@ -397,7 +400,7 @@
/** Map from volume UUID to {@link VersionInfo} */
@Watched
- private WatchedArrayMap<String, VersionInfo> mVersion = new WatchedArrayMap<>();
+ private final WatchedArrayMap<String, VersionInfo> mVersion = new WatchedArrayMap<>();
/**
* Version details for a storage volume that may hold apps.
@@ -435,6 +438,7 @@
}
/** Device identity for the purpose of package verification. */
+ @Watched(manual = true)
private VerifierDeviceIdentity mVerifierDeviceIdentity;
// The user's preferred activities associated with particular intent
@@ -462,10 +466,12 @@
private final WatchedSparseArray<SettingBase> mOtherAppIds;
// For reading/writing settings file.
- private final ArrayList<Signature> mPastSignatures =
- new ArrayList<Signature>();
- private final ArrayMap<Long, Integer> mKeySetRefs =
- new ArrayMap<Long, Integer>();
+ @Watched
+ private final WatchedArrayList<Signature> mPastSignatures =
+ new WatchedArrayList<Signature>();
+ @Watched
+ private final WatchedArrayMap<Long, Integer> mKeySetRefs =
+ new WatchedArrayMap<Long, Integer>();
// Packages that have been renamed since they were first installed.
// Keys are the new names of the packages, values are the original
@@ -495,18 +501,21 @@
* TODO: make this just a local variable that is passed in during package
* scanning to make it less confusing.
*/
- private final ArrayList<PackageSetting> mPendingPackages = new ArrayList<>();
+ @Watched
+ private final WatchedArrayList<PackageSetting> mPendingPackages = new WatchedArrayList<>();
private final File mSystemDir;
- public final KeySetManagerService mKeySetManagerService =
- new KeySetManagerService(mPackages.untrackedStorage());
+ private final KeySetManagerService mKeySetManagerService;
/** Settings and other information about permissions */
+ @Watched(manual = true)
final LegacyPermissionSettings mPermissions;
+ @Watched(manual = true)
private final LegacyPermissionDataProvider mPermissionDataProvider;
+ @Watched(manual = true)
private final DomainVerificationManagerInternal mDomainVerificationManager;
/**
@@ -532,23 +541,7 @@
}};
}
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- public Settings(Map<String, PackageSetting> pkgSettings) {
- mLock = new PackageManagerTracedLock();
- mPackages.putAll(pkgSettings);
- mAppIds = new WatchedArrayList<>();
- mOtherAppIds = new WatchedSparseArray<>();
- mSystemDir = null;
- mPermissions = null;
- mRuntimePermissionsPersistence = null;
- mPermissionDataProvider = null;
- mSettingsFilename = null;
- mBackupSettingsFilename = null;
- mPackageListFilename = null;
- mStoppedPackagesFilename = null;
- mBackupStoppedPackagesFilename = null;
- mKernelMappingFilename = null;
- mDomainVerificationManager = null;
+ private void registerObservers() {
mPackages.registerObserver(mObserver);
mInstallerPackages.registerObserver(mObserver);
mKernelMapping.registerObserver(mObserver);
@@ -564,7 +557,43 @@
mRenamedPackages.registerObserver(mObserver);
mNextAppLinkGeneration.registerObserver(mObserver);
mDefaultBrowserApp.registerObserver(mObserver);
+ mPendingPackages.registerObserver(mObserver);
+ mPastSignatures.registerObserver(mObserver);
+ mKeySetRefs.registerObserver(mObserver);
+ }
+ // CONSTRUCTOR
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public Settings(Map<String, PackageSetting> pkgSettings) {
+ mPackages = new WatchedArrayMap<>();
+ mPackagesSnapshot =
+ new SnapshotCache.Auto<>(mPackages, mPackages, "Settings.mPackages");
+ mKernelMapping = new WatchedArrayMap<>();
+ mKernelMappingSnapshot =
+ new SnapshotCache.Auto<>(mKernelMapping, mKernelMapping, "Settings.mKernelMapping");
+ mInstallerPackages = new WatchedArraySet<>();
+ mInstallerPackagesSnapshot =
+ new SnapshotCache.Auto<>(mInstallerPackages, mInstallerPackages,
+ "Settings.mInstallerPackages");
+ mKeySetManagerService = new KeySetManagerService(mPackages);
+
+ mLock = new PackageManagerTracedLock();
+ mPackages.putAll(pkgSettings);
+ mAppIds = new WatchedArrayList<>();
+ mOtherAppIds = new WatchedSparseArray<>();
+ mSystemDir = null;
+ mPermissions = null;
+ mRuntimePermissionsPersistence = null;
+ mPermissionDataProvider = null;
+ mSettingsFilename = null;
+ mBackupSettingsFilename = null;
+ mPackageListFilename = null;
+ mStoppedPackagesFilename = null;
+ mBackupStoppedPackagesFilename = null;
+ mKernelMappingFilename = null;
+ mDomainVerificationManager = null;
+
+ registerObservers();
Watchable.verifyWatchedAttributes(this, mObserver);
mSnapshot = makeCache();
@@ -574,6 +603,18 @@
LegacyPermissionDataProvider permissionDataProvider,
@NonNull DomainVerificationManagerInternal domainVerificationManager,
@NonNull PackageManagerTracedLock lock) {
+ mPackages = new WatchedArrayMap<>();
+ mPackagesSnapshot =
+ new SnapshotCache.Auto<>(mPackages, mPackages, "Settings.mPackages");
+ mKernelMapping = new WatchedArrayMap<>();
+ mKernelMappingSnapshot =
+ new SnapshotCache.Auto<>(mKernelMapping, mKernelMapping, "Settings.mKernelMapping");
+ mInstallerPackages = new WatchedArraySet<>();
+ mInstallerPackagesSnapshot =
+ new SnapshotCache.Auto<>(mInstallerPackages, mInstallerPackages,
+ "Settings.mInstallerPackages");
+ mKeySetManagerService = new KeySetManagerService(mPackages);
+
mLock = lock;
mAppIds = new WatchedArrayList<>();
mOtherAppIds = new WatchedSparseArray<>();
@@ -602,22 +643,7 @@
mDomainVerificationManager = domainVerificationManager;
- mPackages.registerObserver(mObserver);
- mInstallerPackages.registerObserver(mObserver);
- mKernelMapping.registerObserver(mObserver);
- mDisabledSysPackages.registerObserver(mObserver);
- mBlockUninstallPackages.registerObserver(mObserver);
- mVersion.registerObserver(mObserver);
- mPreferredActivities.registerObserver(mObserver);
- mPersistentPreferredActivities.registerObserver(mObserver);
- mCrossProfileIntentResolvers.registerObserver(mObserver);
- mSharedUsers.registerObserver(mObserver);
- mAppIds.registerObserver(mObserver);
- mOtherAppIds.registerObserver(mObserver);
- mRenamedPackages.registerObserver(mObserver);
- mNextAppLinkGeneration.registerObserver(mObserver);
- mDefaultBrowserApp.registerObserver(mObserver);
-
+ registerObservers();
Watchable.verifyWatchedAttributes(this, mObserver);
mSnapshot = makeCache();
@@ -629,8 +655,13 @@
* are changed by PackageManagerService APIs are deep-copied
*/
private Settings(Settings r) {
- final int mPackagesSize = r.mPackages.size();
- mPackages.putAll(r.mPackages);
+ mPackages = r.mPackagesSnapshot.snapshot();
+ mPackagesSnapshot = new SnapshotCache.Sealed<>();
+ mKernelMapping = r.mKernelMappingSnapshot.snapshot();
+ mKernelMappingSnapshot = new SnapshotCache.Sealed<>();
+ mInstallerPackages = r.mInstallerPackagesSnapshot.snapshot();
+ mInstallerPackagesSnapshot = new SnapshotCache.Sealed<>();
+ mKeySetManagerService = new KeySetManagerService(mPackages);
// The following assignments satisfy Java requirements but are not
// needed by the read-only methods. Note especially that the lock
@@ -647,9 +678,7 @@
mDomainVerificationManager = r.mDomainVerificationManager;
- mInstallerPackages.addAll(r.mInstallerPackages);
- mKernelMapping.putAll(r.mKernelMapping);
- mDisabledSysPackages.putAll(r.mDisabledSysPackages);
+ mDisabledSysPackages.snapshot(r.mDisabledSysPackages);
mBlockUninstallPackages.snapshot(r.mBlockUninstallPackages);
mVersion.putAll(r.mVersion);
mVerifierDeviceIdentity = r.mVerifierDeviceIdentity;
@@ -659,23 +688,26 @@
mPersistentPreferredActivities, r.mPersistentPreferredActivities);
WatchedSparseArray.snapshot(
mCrossProfileIntentResolvers, r.mCrossProfileIntentResolvers);
- mSharedUsers.putAll(r.mSharedUsers);
+ mSharedUsers.snapshot(r.mSharedUsers);
mAppIds = r.mAppIds.snapshot();
mOtherAppIds = r.mOtherAppIds.snapshot();
- mPastSignatures.addAll(r.mPastSignatures);
- mKeySetRefs.putAll(r.mKeySetRefs);
+ WatchedArrayList.snapshot(
+ mPastSignatures, r.mPastSignatures);
+ WatchedArrayMap.snapshot(
+ mKeySetRefs, r.mKeySetRefs);
mRenamedPackages.snapshot(r.mRenamedPackages);
mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration);
mDefaultBrowserApp.snapshot(r.mDefaultBrowserApp);
// mReadMessages
- mPendingPackages.addAll(r.mPendingPackages);
+ WatchedArrayList.snapshot(
+ mPendingPackages, r.mPendingPackages);
mSystemDir = null;
// mKeySetManagerService;
mPermissions = r.mPermissions;
mPermissionDataProvider = r.mPermissionDataProvider;
// Do not register any Watchables and do not create a snapshot cache.
- mSnapshot = null;
+ mSnapshot = new SnapshotCache.Sealed();
}
/**
@@ -2326,7 +2358,7 @@
serializer.startTag(null, "shared-user");
serializer.attribute(null, ATTR_NAME, usr.name);
serializer.attributeInt(null, "userId", usr.userId);
- usr.signatures.writeXml(serializer, "sigs", mPastSignatures);
+ usr.signatures.writeXml(serializer, "sigs", mPastSignatures.untrackedStorage());
serializer.endTag(null, "shared-user");
}
@@ -2736,11 +2768,11 @@
writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
- pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
+ pkg.signatures.writeXml(serializer, "sigs", mPastSignatures.untrackedStorage());
if (installSource.initiatingPackageSignatures != null) {
installSource.initiatingPackageSignatures.writeXml(
- serializer, "install-initiator-sigs", mPastSignatures);
+ serializer, "install-initiator-sigs", mPastSignatures.untrackedStorage());
}
writeSigningKeySetLPr(serializer, pkg.keySetData);
@@ -2909,7 +2941,7 @@
} else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) {
// No longer used.
} else if (tagName.equals("keyset-settings")) {
- mKeySetManagerService.readKeySetsLPw(parser, mKeySetRefs);
+ mKeySetManagerService.readKeySetsLPw(parser, mKeySetRefs.untrackedStorage());
} else if (TAG_VERSION.equals(tagName)) {
final String volumeUuid = XmlUtils.readStringAttribute(parser,
ATTR_VOLUME_UUID);
@@ -3697,7 +3729,7 @@
} else if (tagName.equals(TAG_ENABLED_COMPONENTS)) {
readEnabledComponentsLPw(packageSetting, parser, 0);
} else if (tagName.equals("sigs")) {
- packageSetting.signatures.readXml(parser, mPastSignatures);
+ packageSetting.signatures.readXml(parser, mPastSignatures.untrackedStorage());
} else if (tagName.equals(TAG_PERMISSIONS)) {
readInstallPermissionsLPr(parser,
packageSetting.getLegacyPermissionState(), users);
@@ -3728,7 +3760,7 @@
packageSetting.keySetData.addDefinedKeySet(id, alias);
} else if (tagName.equals("install-initiator-sigs")) {
final PackageSignatures signatures = new PackageSignatures();
- signatures.readXml(parser, mPastSignatures);
+ signatures.readXml(parser, mPastSignatures.untrackedStorage());
packageSetting.installSource =
packageSetting.installSource.setInitiatingPackageSignatures(signatures);
} else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
@@ -3923,7 +3955,7 @@
String tagName = parser.getName();
if (tagName.equals("sigs")) {
- su.signatures.readXml(parser, mPastSignatures);
+ su.signatures.readXml(parser, mPastSignatures.untrackedStorage());
} else if (tagName.equals("perms")) {
readInstallPermissionsLPr(parser, su.getLegacyPermissionState(), users);
} else {
diff --git a/services/core/java/com/android/server/pm/SnapshotStatistics.java b/services/core/java/com/android/server/pm/SnapshotStatistics.java
index c425bad5..7bf00603 100644
--- a/services/core/java/com/android/server/pm/SnapshotStatistics.java
+++ b/services/core/java/com/android/server/pm/SnapshotStatistics.java
@@ -23,6 +23,7 @@
import android.os.SystemClock;
import android.text.TextUtils;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.EventLogTags;
import java.io.PrintWriter;
@@ -239,6 +240,11 @@
public int mTotalUsed = 0;
/**
+ * The total number of times a snapshot was bypassed because corking was in effect.
+ */
+ public int mTotalCorked = 0;
+
+ /**
* The total number of builds that count as big, which means they took longer than
* SNAPSHOT_BIG_BUILD_TIME_NS.
*/
@@ -291,6 +297,13 @@
}
}
+ /**
+ * Record a cork.
+ */
+ private void corked() {
+ mTotalCorked++;
+ }
+
private Stats(long now) {
mStartTimeUs = now;
mTimes = new int[mTimeBins.count()];
@@ -308,6 +321,7 @@
mUsed = Arrays.copyOf(orig.mUsed, orig.mUsed.length);
mTotalBuilds = orig.mTotalBuilds;
mTotalUsed = orig.mTotalUsed;
+ mTotalCorked = orig.mTotalCorked;
mBigBuilds = orig.mBigBuilds;
mShortLived = orig.mShortLived;
mTotalTimeUs = orig.mTotalTimeUs;
@@ -365,6 +379,7 @@
* Dump the summary statistics record. Choose the header or the data.
* number of builds
* number of uses
+ * number of corks
* number of big builds
* number of short lifetimes
* cumulative build time, in seconds
@@ -373,13 +388,13 @@
private void dumpStats(PrintWriter pw, String indent, long now, boolean header) {
dumpPrefix(pw, indent, now, header, "Summary stats");
if (header) {
- pw.format(Locale.US, " %10s %10s %10s %10s %10s %10s",
- "TotBlds", "TotUsed", "BigBlds", "ShortLvd",
+ pw.format(Locale.US, " %10s %10s %10s %10s %10s %10s %10s",
+ "TotBlds", "TotUsed", "TotCork", "BigBlds", "ShortLvd",
"TotTime", "MaxTime");
} else {
pw.format(Locale.US,
- " %10d %10d %10d %10d %10d %10d",
- mTotalBuilds, mTotalUsed, mBigBuilds, mShortLived,
+ " %10d %10d %10d %10d %10d %10d %10d",
+ mTotalBuilds, mTotalUsed, mTotalCorked, mBigBuilds, mShortLived,
mTotalTimeUs / 1000, mMaxBuildTimeUs / 1000);
}
pw.println();
@@ -516,7 +531,7 @@
* @param done The time at which the snapshot rebuild completed, in ns.
* @param hits The number of times the previous snapshot was used.
*/
- public void rebuild(long now, long done, int hits) {
+ public final void rebuild(long now, long done, int hits) {
// The duration has a span of about 2000s
final int duration = (int) (done - now);
boolean reportEvent = false;
@@ -544,9 +559,20 @@
}
/**
+ * Record a corked snapshot request.
+ */
+ public final void corked() {
+ synchronized (mLock) {
+ mShort[0].corked();
+ mLong[0].corked();
+ }
+ }
+
+ /**
* Roll a stats array. Shift the elements up an index and create a new element at
* index zero. The old element zero is completed with the specified time.
*/
+ @GuardedBy("mLock")
private void shift(Stats[] s, long now) {
s[0].complete(now);
for (int i = s.length - 1; i > 0; i--) {
@@ -598,7 +624,8 @@
* Dump the statistics. The format is compatible with the PackageManager dumpsys
* output.
*/
- public void dump(PrintWriter pw, String indent, long now, int unrecorded, boolean full) {
+ public void dump(PrintWriter pw, String indent, long now, int unrecorded,
+ int corkLevel, boolean full) {
// Grab the raw statistics under lock, but print them outside of the lock.
Stats[] l;
Stats[] s;
@@ -608,7 +635,8 @@
s = Arrays.copyOf(mShort, mShort.length);
s[0] = new Stats(s[0]);
}
- pw.format(Locale.US, "%s Unrecorded hits %d", indent, unrecorded);
+ pw.format(Locale.US, "%s Unrecorded-hits: %d Cork-level: %d", indent,
+ unrecorded, corkLevel);
pw.println();
dump(pw, indent, now, l, s, "stats");
if (!full) {
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 90a3c58..b7a069e 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -151,6 +151,9 @@
"include-filter": "com.android.server.pm.parsing.SystemPartitionParseTest"
}
]
+ },
+ {
+ "name": "CtsPackageManagerBootTestCases"
}
],
"imports": [
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 50f958f..a7bac20 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -37,6 +37,7 @@
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.media.RingtoneManager;
+import android.media.midi.MidiManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
@@ -866,6 +867,11 @@
grantPermissionsToSystemPackage(pm, systemCaptionsServicePackageName, userId,
MICROPHONE_PERMISSIONS);
}
+
+ // Bluetooth MIDI Service
+ grantSystemFixedPermissionsToSystemPackage(pm,
+ MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, userId,
+ NEARBY_DEVICES_PERMISSIONS);
}
private String getDefaultSystemHandlerActivityPackageForCategory(PackageManagerWrapper pm,
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index 68e7bdb..09f8941 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -179,7 +179,7 @@
true, /* enableQuickDoze */
true, /* forceAllAppsStandby */
true, /* forceBackgroundCheck */
- PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF, /* locationMode */
+ PowerManager.LOCATION_MODE_FOREGROUND_ONLY, /* locationMode */
PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY /* soundTriggerMode */
);
diff --git a/services/core/java/com/android/server/utils/SnapshotCache.java b/services/core/java/com/android/server/utils/SnapshotCache.java
index b4b8835..42b9b23 100644
--- a/services/core/java/com/android/server/utils/SnapshotCache.java
+++ b/services/core/java/com/android/server/utils/SnapshotCache.java
@@ -19,6 +19,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
/**
* A class that caches snapshots. Instances are instantiated on a {@link Watchable}; when the
* {@link Watchable} reports a change, the cache is cleared. The snapshot() method fetches the
@@ -35,25 +38,65 @@
*/
private static final boolean ENABLED = true;
+ /**
+ * The statistics for a single cache. The object records the number of times a
+ * snapshot was reused and the number of times a snapshot was rebuilt.
+ */
+ private static class Statistics {
+ final String mName;
+ private final AtomicInteger mReused = new AtomicInteger(0);
+ private final AtomicInteger mRebuilt = new AtomicInteger(0);
+ Statistics(@NonNull String n) {
+ mName = n;
+ }
+ }
+
// The source object from which snapshots are created. This may be null if createSnapshot()
// does not require it.
protected final T mSource;
// The cached snapshot
- private T mSnapshot = null;
+ private volatile T mSnapshot = null;
// True if the snapshot is sealed and may not be modified.
- private boolean mSealed = false;
+ private volatile boolean mSealed = false;
+
+ // The statistics for this cache. This may be null.
+ private final Statistics mStatistics;
+
+ /**
+ * The global list of caches.
+ */
+ private static final WeakHashMap<SnapshotCache, Void> sCaches = new WeakHashMap<>();
/**
* Create a cache with a source object for rebuilding snapshots and a
- * {@link Watchable} that notifies when the cache is invalid.
+ * {@link Watchable} that notifies when the cache is invalid. If the name is null
+ * then statistics are not collected for this cache.
+ * @param source Source data for rebuilding snapshots.
+ * @param watchable The object that notifies when the cache is invalid.
+ * @param name The name of the cache, for statistics reporting.
+ */
+ public SnapshotCache(@Nullable T source, @NonNull Watchable watchable, @Nullable String name) {
+ mSource = source;
+ watchable.registerObserver(this);
+ if (name != null) {
+ mStatistics = new Statistics(name);
+ sCaches.put(this, null);
+ } else {
+ mStatistics = null;
+ }
+ }
+
+ /**
+ * Create a cache with a source object for rebuilding snapshots and a
+ * {@link Watchable} that notifies when the cache is invalid. The name is null in
+ * this API.
* @param source Source data for rebuilding snapshots.
* @param watchable The object that notifies when the cache is invalid.
*/
public SnapshotCache(@Nullable T source, @NonNull Watchable watchable) {
- mSource = source;
- watchable.registerObserver(this);
+ this(source, watchable, null);
}
/**
@@ -63,13 +106,14 @@
public SnapshotCache() {
mSource = null;
mSealed = true;
+ mStatistics = null;
}
/**
* Notify the object that the source object has changed. If the local object is sealed then
* IllegalStateException is thrown. Otherwise, the cache is cleared.
*/
- public void onChange(@Nullable Watchable what) {
+ public final void onChange(@Nullable Watchable what) {
if (mSealed) {
throw new IllegalStateException("attempt to change a sealed object");
}
@@ -79,7 +123,7 @@
/**
* Seal the cache. Attempts to modify the cache will generate an exception.
*/
- public void seal() {
+ public final void seal() {
mSealed = true;
}
@@ -88,11 +132,14 @@
* new snapshot and saves it in the cache.
* @return A snapshot as returned by createSnapshot() and possibly cached.
*/
- public T snapshot() {
+ public final T snapshot() {
T s = mSnapshot;
if (s == null || !ENABLED) {
s = createSnapshot();
mSnapshot = s;
+ if (mStatistics != null) mStatistics.mRebuilt.incrementAndGet();
+ } else {
+ if (mStatistics != null) mStatistics.mReused.incrementAndGet();
}
return s;
}
@@ -123,4 +170,25 @@
throw new UnsupportedOperationException("cannot snapshot a sealed snaphot");
}
}
+
+ /**
+ * A snapshot cache suitable for Snappable types. The key is that Snappable types
+ * have a known implementation of createSnapshot() so that this class is concrete.
+ * @param <T> The class whose snapshot is being cached.
+ */
+ public static class Auto<T extends Snappable<T>> extends SnapshotCache<T> {
+ public Auto(@NonNull T source, @NonNull Watchable watchable, @Nullable String name) {
+ super(source, watchable, name);
+ }
+ public Auto(@NonNull T source, @NonNull Watchable watchable) {
+ this(source, watchable, null);
+ }
+ /**
+ * Concrete createSnapshot() using the snapshot() method of <T>.
+ */
+ public T createSnapshot() {
+ return mSource.snapshot();
+ }
+ }
+
}
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index 8818023..3bdeec0 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -158,8 +158,15 @@
* carrier owned networks may be selected, as the request specifies only subIds in the VCN's
* subscription group, while the VCN networks are excluded by virtue of not having subIds set on
* the VCN-exposed networks.
+ *
+ * <p>If the VCN that this UnderlyingNetworkTracker belongs to is in test-mode, this will return
+ * a NetworkRequest that only matches Test Networks.
*/
private NetworkRequest getRouteSelectionRequest() {
+ if (mVcnContext.isInTestMode()) {
+ return getTestNetworkRequest(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup));
+ }
+
return getBaseNetworkRequestBuilder()
.setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
.build();
@@ -210,6 +217,15 @@
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
}
+ /** Builds and returns a NetworkRequest for the given subIds to match Test Networks. */
+ private NetworkRequest getTestNetworkRequest(@NonNull Set<Integer> subIds) {
+ return new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ .setSubscriptionIds(subIds)
+ .build();
+ }
+
/**
* Update this UnderlyingNetworkTracker's TelephonySubscriptionSnapshot.
*
diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java
index 7399e56..d958222 100644
--- a/services/core/java/com/android/server/vcn/VcnContext.java
+++ b/services/core/java/com/android/server/vcn/VcnContext.java
@@ -31,14 +31,17 @@
@NonNull private final Context mContext;
@NonNull private final Looper mLooper;
@NonNull private final VcnNetworkProvider mVcnNetworkProvider;
+ private final boolean mIsInTestMode;
public VcnContext(
@NonNull Context context,
@NonNull Looper looper,
- @NonNull VcnNetworkProvider vcnNetworkProvider) {
+ @NonNull VcnNetworkProvider vcnNetworkProvider,
+ boolean isInTestMode) {
mContext = Objects.requireNonNull(context, "Missing context");
mLooper = Objects.requireNonNull(looper, "Missing looper");
mVcnNetworkProvider = Objects.requireNonNull(vcnNetworkProvider, "Missing networkProvider");
+ mIsInTestMode = isInTestMode;
}
@NonNull
@@ -56,6 +59,10 @@
return mVcnNetworkProvider;
}
+ public boolean isInTestMode() {
+ return mIsInTestMode;
+ }
+
/**
* Verifies that the caller is running on the VcnContext Thread.
*
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9f51d97..8c4b75b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2483,39 +2483,52 @@
}
/**
- * @return whether this activity supports split-screen multi-window and can be put in the docked
- * root task.
+ * @return whether this activity supports split-screen multi-window and can be put in
+ * split-screen.
*/
@Override
public boolean supportsSplitScreenWindowingMode() {
- // An activity can not be docked even if it is considered resizeable because it only
- // supports picture-in-picture mode but has a non-resizeable resizeMode
+ return supportsSplitScreenWindowingModeInDisplayArea(getDisplayArea());
+ }
+
+ /**
+ * @return whether this activity supports split-screen multi-window and can be put in
+ * split-screen if it is in the given {@link TaskDisplayArea}.
+ */
+ boolean supportsSplitScreenWindowingModeInDisplayArea(@Nullable TaskDisplayArea tda) {
return super.supportsSplitScreenWindowingMode()
- && mAtmService.mSupportsSplitScreenMultiWindow && supportsMultiWindow();
+ && mAtmService.mSupportsSplitScreenMultiWindow
+ && supportsMultiWindowInDisplayArea(tda);
+ }
+
+ boolean supportsFreeform() {
+ return supportsFreeformInDisplayArea(getDisplayArea());
}
/**
* @return whether this activity supports freeform multi-window and can be put in the freeform
- * root task.
+ * windowing mode if it is in the given {@link TaskDisplayArea}.
*/
- boolean supportsFreeform() {
- return mAtmService.mSupportsFreeformWindowManagement && supportsMultiWindow();
+ boolean supportsFreeformInDisplayArea(@Nullable TaskDisplayArea tda) {
+ return mAtmService.mSupportsFreeformWindowManagement
+ && supportsMultiWindowInDisplayArea(tda);
+ }
+
+ boolean supportsMultiWindow() {
+ return supportsMultiWindowInDisplayArea(getDisplayArea());
}
/**
- * @return whether this activity supports multi-window.
+ * @return whether this activity supports multi-window if it is in the given
+ * {@link TaskDisplayArea}.
*/
- boolean supportsMultiWindow() {
- return mAtmService.mSupportsMultiWindow && !isActivityTypeHome()
- && (isResizeable() || mAtmService.mDevEnableNonResizableMultiWindow);
- }
-
- // TODO(b/176061101) replace supportsMultiWindow() after fixing tests.
- boolean supportsMultiWindow2() {
+ boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) {
+ if (isActivityTypeHome()) {
+ return false;
+ }
if (!mAtmService.mSupportsMultiWindow) {
return false;
}
- final TaskDisplayArea tda = getDisplayArea();
if (tda == null) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 8583061..4edcfa9 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1684,7 +1684,8 @@
// Leave the task in its current root task or a fullscreen root task if it isn't
// resizeable and the preferred root task is in multi-window mode.
- if (inMultiWindowMode && !task.supportsMultiWindow()) {
+ if (inMultiWindowMode
+ && !task.supportsMultiWindowInDisplayArea(rootTask.getDisplayArea())) {
Slog.w(TAG, "Can not move unresizeable task=" + task + " to multi-window root task="
+ rootTask + " Moving to a fullscreen root task instead.");
if (prevRootTask != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 30f69dd79..37e15c7 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -92,6 +92,7 @@
import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
@@ -2667,7 +2668,8 @@
if (oldImmersiveMode != newImmersiveMode) {
mLastImmersiveMode = newImmersiveMode;
// The immersive confirmation window should be attached to the immersive window root.
- final int rootDisplayAreaId = win.getRootDisplayArea().mFeatureId;
+ final RootDisplayArea root = win.getRootDisplayArea();
+ final int rootDisplayAreaId = root == null ? FEATURE_UNDEFINED : root.mFeatureId;
mImmersiveModeConfirmation.immersiveModeChangedLw(rootDisplayAreaId, newImmersiveMode,
mService.mPolicy.isUserSetupComplete(),
isNavBarEmpty(disableFlags));
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ab7e65c..0879ddd 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3046,7 +3046,7 @@
// There is a 1-to-1 relationship between root task and task when not in
// primary split-windowing mode.
if (task.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- && r.supportsSplitScreenWindowingMode()
+ && r.supportsSplitScreenWindowingModeInDisplayArea(task.getDisplayArea())
&& (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
|| windowingMode == WINDOWING_MODE_UNDEFINED)) {
return true;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index be01173..f3368c1 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -61,6 +61,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.SurfaceControl.METADATA_TASK_ID;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
@@ -1670,6 +1671,14 @@
return isUidPresent;
}
+ ActivityRecord topActivityContainsStartingWindow() {
+ if (getParent() == null) {
+ return null;
+ }
+ return getActivity((r) -> r.getWindow(window ->
+ window.getBaseType() == TYPE_APPLICATION_STARTING) != null);
+ }
+
ActivityRecord topActivityWithStartingWindow() {
if (getParent() == null) {
return null;
@@ -1977,32 +1986,46 @@
@Override
public boolean supportsSplitScreenWindowingMode() {
- final Task topTask = getTopMostTask();
- return super.supportsSplitScreenWindowingMode()
- && (topTask == null || topTask.supportsSplitScreenWindowingModeInner());
+ return supportsSplitScreenWindowingModeInDisplayArea(getDisplayArea());
}
- private boolean supportsSplitScreenWindowingModeInner() {
+ boolean supportsSplitScreenWindowingModeInDisplayArea(@Nullable TaskDisplayArea tda) {
+ final Task topTask = getTopMostTask();
+ return super.supportsSplitScreenWindowingMode()
+ && (topTask == null || topTask.supportsSplitScreenWindowingModeInner(tda));
+ }
+
+ private boolean supportsSplitScreenWindowingModeInner(@Nullable TaskDisplayArea tda) {
return super.supportsSplitScreenWindowingMode()
&& mAtmService.mSupportsSplitScreenMultiWindow
- && supportsMultiWindow();
+ && supportsMultiWindowInDisplayArea(tda);
}
boolean supportsFreeform() {
- return mAtmService.mSupportsFreeformWindowManagement && supportsMultiWindow();
+ return supportsFreeformInDisplayArea(getDisplayArea());
+ }
+
+ /**
+ * @return whether this task supports freeform multi-window if it is in the given
+ * {@link TaskDisplayArea}.
+ */
+ boolean supportsFreeformInDisplayArea(@Nullable TaskDisplayArea tda) {
+ return mAtmService.mSupportsFreeformWindowManagement
+ && supportsMultiWindowInDisplayArea(tda);
}
boolean supportsMultiWindow() {
- return mAtmService.mSupportsMultiWindow
- && (isResizeable() || mAtmService.mDevEnableNonResizableMultiWindow);
+ return supportsMultiWindowInDisplayArea(getDisplayArea());
}
- // TODO(b/176061101) replace supportsMultiWindow() after fixing tests.
- boolean supportsMultiWindow2() {
+ /**
+ * @return whether this task supports multi-window if it is in the given
+ * {@link TaskDisplayArea}.
+ */
+ boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) {
if (!mAtmService.mSupportsMultiWindow) {
return false;
}
- final TaskDisplayArea tda = getDisplayArea();
if (tda == null) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 368e6dd..ccfdb8c 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1614,18 +1614,18 @@
boolean supportsPip = mAtmService.mSupportsPictureInPicture;
if (supportsMultiWindow) {
if (task != null) {
- supportsSplitScreen = task.supportsSplitScreenWindowingMode();
- supportsFreeform = task.supportsFreeform();
- supportsMultiWindow = task.supportsMultiWindow()
+ supportsSplitScreen = task.supportsSplitScreenWindowingModeInDisplayArea(this);
+ supportsFreeform = task.supportsFreeformInDisplayArea(this);
+ supportsMultiWindow = task.supportsMultiWindowInDisplayArea(this)
// When the activity needs to be moved to PIP while the Task is not in PIP,
// it can be moved to a new created PIP Task, so WINDOWING_MODE_PINNED is
// always valid for Task as long as the device supports it.
|| (windowingMode == WINDOWING_MODE_PINNED && supportsPip);
} else if (r != null) {
- supportsSplitScreen = r.supportsSplitScreenWindowingMode();
- supportsFreeform = r.supportsFreeform();
+ supportsSplitScreen = r.supportsSplitScreenWindowingModeInDisplayArea(this);
+ supportsFreeform = r.supportsFreeformInDisplayArea(this);
supportsPip = r.supportsPictureInPicture();
- supportsMultiWindow = r.supportsMultiWindow();
+ supportsMultiWindow = r.supportsMultiWindowInDisplayArea(this);
}
}
@@ -2078,14 +2078,15 @@
task.finishAllActivitiesImmediately();
} else {
// Reparent task to corresponding launch root or display area.
- final WindowContainer launchRoot = task.supportsSplitScreenWindowingMode()
- ? toDisplayArea.getLaunchRootTask(
- task.getWindowingMode(),
- task.getActivityType(),
- null /* options */,
- null /* sourceTask */,
- 0 /* launchFlags */)
- : null;
+ final WindowContainer launchRoot =
+ task.supportsSplitScreenWindowingModeInDisplayArea(toDisplayArea)
+ ? toDisplayArea.getLaunchRootTask(
+ task.getWindowingMode(),
+ task.getActivityType(),
+ null /* options */,
+ null /* sourceTask */,
+ 0 /* launchFlags */)
+ : null;
task.reparent(launchRoot == null ? toDisplayArea : launchRoot, POSITION_TOP);
// Set the windowing mode to undefined by default to let the root task inherited the
@@ -2101,7 +2102,8 @@
if (lastReparentedRootTask != null) {
if (toDisplayArea.isSplitScreenModeActivated()
- && !lastReparentedRootTask.supportsSplitScreenWindowingMode()) {
+ && !lastReparentedRootTask.supportsSplitScreenWindowingModeInDisplayArea(
+ toDisplayArea)) {
// Dismiss split screen if the last reparented root task doesn't support split mode.
mAtmService.getTaskChangeNotificationController()
.notifyActivityDismissingDockedRootTask();
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 0bc7999..cc0471c 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -612,7 +612,7 @@
private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity,
TaskDisplayArea displayArea) {
- if (!activity.supportsFreeform() || activity.isResizeable()) {
+ if (!activity.supportsFreeformInDisplayArea(displayArea) || activity.isResizeable()) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index fb481b4..cc8ee60 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -184,7 +184,7 @@
Rect mainFrame = null;
final boolean playShiftUpAnimation = !task.inMultiWindowMode();
if (prepareAnimation && playShiftUpAnimation) {
- final ActivityRecord topActivity = task.topActivityWithStartingWindow();
+ final ActivityRecord topActivity = task.topActivityContainsStartingWindow();
if (topActivity != null) {
final WindowState mainWindow =
topActivity.findMainWindow(false/* includeStartingApp */);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index c29211f..2ff3683 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -569,14 +569,15 @@
if (newParent.asTaskDisplayArea() != null) {
// For now, reparenting to displayarea is different from other reparents...
as.reparent(newParent.asTaskDisplayArea(), hop.getToTop());
- } else {
+ } else if (newParent.asTask() != null) {
if (newParent.inMultiWindowMode() && task.isLeafTask()) {
if (newParent.inPinnedWindowingMode()) {
Slog.w(TAG, "Can't support moving a task to another PIP window..."
+ " newParent=" + newParent + " task=" + task);
return 0;
}
- if (!task.supportsMultiWindow()) {
+ if (!task.supportsMultiWindowInDisplayArea(
+ newParent.asTask().getDisplayArea())) {
Slog.w(TAG, "Can't support task that doesn't support multi-window"
+ " mode in multi-window mode... newParent=" + newParent
+ " task=" + task);
@@ -586,6 +587,9 @@
task.reparent((Task) newParent,
hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
false /*moveParents*/, "sanitizeAndApplyHierarchyOp");
+ } else {
+ throw new RuntimeException("Can only reparent task to another task or"
+ + " taskDisplayArea, but not " + newParent);
}
} else {
final Task rootTask = (Task) (
@@ -641,6 +645,9 @@
}
final boolean newParentInMultiWindow = newParent.inMultiWindowMode();
+ final TaskDisplayArea newParentTda = newParent.asTask() != null
+ ? newParent.asTask().getDisplayArea()
+ : newParent.asTaskDisplayArea();
final WindowContainer finalCurrentParent = currentParent;
Slog.i(TAG, "reparentChildrenTasksHierarchyOp"
+ " currentParent=" + currentParent + " newParent=" + newParent + " hop=" + hop);
@@ -656,7 +663,7 @@
// are reparenting from.
return;
}
- if (newParentInMultiWindow && !task.supportsMultiWindow()) {
+ if (newParentInMultiWindow && !task.supportsMultiWindowInDisplayArea(newParentTda)) {
Slog.e(TAG, "reparentChildrenTasksHierarchyOp non-resizeable task to multi window,"
+ " task=" + task);
return;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 540035f..b121bfc 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2412,14 +2412,14 @@
}
if (startingWindow && StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
- // cancel the remove starting window animation on shell
+ // Cancel the remove starting window animation on shell. The main window might changed
+ // during animating, checking for all windows would be safer.
if (mActivityRecord != null) {
- final WindowState mainWindow =
- mActivityRecord.findMainWindow(false/* includeStartingApp */);
- if (mainWindow != null && mainWindow.isSelfAnimating(0 /* flags */,
- ANIMATION_TYPE_STARTING_REVEAL)) {
- mainWindow.cancelAnimation();
- }
+ mActivityRecord.forAllWindows(w -> {
+ if (w.isSelfAnimating(0, ANIMATION_TYPE_STARTING_REVEAL)) {
+ w.cancelAnimation();
+ }
+ }, true);
}
}
@@ -2682,6 +2682,13 @@
}
}
+ /**
+ * Move the touch gesture from the currently touched window on this display to this window.
+ */
+ public boolean transferTouch() {
+ return mWmService.mInputManager.transferTouch(mInputChannelToken);
+ }
+
void disposeInputChannel() {
if (mDeadWindowEventReceiver != null) {
mDeadWindowEventReceiver.dispose();
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index cca62b9..eb9c8db 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1834,8 +1834,19 @@
}
}
-static void nativeSetPointerSpeed(JNIEnv* /* env */,
- jclass /* clazz */, jlong ptr, jint speed) {
+static jboolean nativeTransferTouch(JNIEnv* env, jclass /* clazz */, jlong ptr,
+ jobject destChannelTokenObj) {
+ sp<IBinder> destChannelToken = ibinderForJavaObject(env, destChannelTokenObj);
+
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+ if (im->getInputManager()->getDispatcher()->transferTouch(destChannelToken)) {
+ return JNI_TRUE;
+ } else {
+ return JNI_FALSE;
+ }
+}
+
+static void nativeSetPointerSpeed(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jint speed) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
im->setPointerSpeed(speed);
@@ -2308,6 +2319,7 @@
{"nativeSetSystemUiLightsOut", "(JZ)V", (void*)nativeSetSystemUiLightsOut},
{"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;Z)Z",
(void*)nativeTransferTouchFocus},
+ {"nativeTransferTouch", "(JLandroid/os/IBinder;)Z", (void*)nativeTransferTouch},
{"nativeSetPointerSpeed", "(JI)V", (void*)nativeSetPointerSpeed},
{"nativeSetShowTouches", "(JZ)V", (void*)nativeSetShowTouches},
{"nativeSetInteractive", "(JZ)V", (void*)nativeSetInteractive},
diff --git a/services/net/TEST_MAPPING b/services/net/TEST_MAPPING
index 7025dd1..7eca1f9 100644
--- a/services/net/TEST_MAPPING
+++ b/services/net/TEST_MAPPING
@@ -2,6 +2,9 @@
"imports": [
{
"path": "frameworks/base/core/java/android/net"
+ },
+ {
+ "path": "packages/modules/Wifi/framework"
}
]
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
new file mode 100644
index 0000000..b8fbe34
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 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.biometrics.sensors.face.aidl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.face.ISession;
+import android.os.Handler;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.sensors.LockoutCache;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+
+@Presubmit
+@SmallTest
+public class SensorTest {
+
+ private static final String TAG = "SensorTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+ private static final byte[] HAT = new byte[69];
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private IBiometricService mBiometricService;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback;
+ @Mock
+ private Sensor.HalSessionCallback.Callback mHalSessionCallback;
+ @Mock
+ private LockoutResetDispatcher mLockoutResetDispatcher;
+
+ private final TestLooper mLooper = new TestLooper();
+ private final LockoutCache mLockoutCache = new LockoutCache();
+
+ private UserAwareBiometricScheduler mScheduler;
+ private Sensor.HalSessionCallback mHalCallback;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
+
+ mScheduler = new UserAwareBiometricScheduler(TAG,
+ null /* gestureAvailabilityDispatcher */,
+ () -> USER_ID,
+ mUserSwitchCallback);
+ mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
+ TAG, mScheduler, SENSOR_ID,
+ USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback);
+ }
+
+ @Test
+ public void halSessionCallback_respondsToResetLockout() throws Exception {
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ mHalCallback.onLockoutCleared();
+ return null;
+ }).when(mSession).resetLockout(any());
+ mLockoutCache.setLockoutModeForUser(USER_ID, LockoutTracker.LOCKOUT_TIMED);
+
+ mScheduler.scheduleClientMonitor(new FaceResetLockoutClient(mContext,
+ () -> mSession, USER_ID, TAG, SENSOR_ID, HAT, mLockoutCache,
+ mLockoutResetDispatcher));
+ mLooper.dispatchAll();
+
+ verifyNotLocked();
+ }
+
+ @Test
+ public void halSessionCallback_respondsToUnprovokedResetLockout() {
+ mLockoutCache.setLockoutModeForUser(USER_ID, LockoutTracker.LOCKOUT_TIMED);
+
+ mHalCallback.onLockoutCleared();
+ mLooper.dispatchAll();
+
+ verifyNotLocked();
+ }
+
+ private void verifyNotLocked() {
+ assertEquals(LockoutTracker.LOCKOUT_NONE, mLockoutCache.getLockoutModeForUser(USER_ID));
+ verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(eq(SENSOR_ID));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
new file mode 100644
index 0000000..5dfc248
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 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.biometrics.sensors.fingerprint.aidl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.os.Handler;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.sensors.LockoutCache;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+
+@Presubmit
+@SmallTest
+public class SensorTest {
+
+ private static final String TAG = "SensorTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+ private static final byte[] HAT = new byte[69];
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private IBiometricService mBiometricService;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback;
+ @Mock
+ private Sensor.HalSessionCallback.Callback mHalSessionCallback;
+ @Mock
+ private LockoutResetDispatcher mLockoutResetDispatcher;
+
+ private final TestLooper mLooper = new TestLooper();
+ private final LockoutCache mLockoutCache = new LockoutCache();
+
+ private UserAwareBiometricScheduler mScheduler;
+ private Sensor.HalSessionCallback mHalCallback;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
+
+ mScheduler = new UserAwareBiometricScheduler(TAG,
+ null /* gestureAvailabilityDispatcher */,
+ () -> USER_ID,
+ mUserSwitchCallback);
+ mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
+ TAG, mScheduler, SENSOR_ID,
+ USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback);
+ }
+
+ @Test
+ public void halSessionCallback_respondsToResetLockout() throws Exception {
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ mHalCallback.onLockoutCleared();
+ return null;
+ }).when(mSession).resetLockout(any());
+ mLockoutCache.setLockoutModeForUser(USER_ID, LockoutTracker.LOCKOUT_TIMED);
+
+ mScheduler.scheduleClientMonitor(new FingerprintResetLockoutClient(mContext,
+ () -> mSession, USER_ID, TAG, SENSOR_ID, HAT, mLockoutCache,
+ mLockoutResetDispatcher));
+ mLooper.dispatchAll();
+
+ verifyNotLocked();
+ }
+
+ @Test
+ public void halSessionCallback_respondsToUnprovokedResetLockout() {
+ mLockoutCache.setLockoutModeForUser(USER_ID, LockoutTracker.LOCKOUT_TIMED);
+
+ mHalCallback.onLockoutCleared();
+ mLooper.dispatchAll();
+
+ verifyNotLocked();
+ }
+
+ private void verifyNotLocked() {
+ assertEquals(LockoutTracker.LOCKOUT_NONE, mLockoutCache.getLockoutModeForUser(USER_ID));
+ verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(eq(SENSOR_ID));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index 5caff3d..7003ef7 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -92,7 +92,7 @@
}
@Override
- public void tryToCreateTypeface(File file) throws IOException {
+ public void tryToCreateTypeface(File file) throws Throwable {
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index c08857c..6cc8d471 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -68,10 +68,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(mContextSpy) {
@@ -97,7 +99,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index ee9de07..97bd066 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -70,10 +70,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
HdmiControlService hdmiControlService =
@@ -89,7 +91,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index d5df071..29c9b40 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -71,10 +71,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
HdmiControlService hdmiControlService =
@@ -85,7 +87,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
index 4f97c26..650ffe9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
@@ -80,10 +80,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(mContextSpy) {
@@ -109,7 +111,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
index 678f8b2..fa5cb67 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
@@ -103,8 +103,6 @@
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mMyLooper));
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@@ -129,7 +127,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(context, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mMyLooper));
}
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index 45409c8..29f62b5 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -30,6 +30,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.ContextWrapper;
@@ -96,9 +97,10 @@
mContextSpy = spy(new ContextWrapper(
InstrumentationRegistry.getInstrumentation().getTargetContext()));
- PowerManager powerManager = new PowerManager(
- mContextSpy, mIPowerManagerMock, mIThermalServiceMock, new Handler(mLooper));
- doReturn(powerManager).when(mContextSpy).getSystemService(Context.POWER_SERVICE);
+
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mLooper)));
doReturn(true).when(mIPowerManagerMock).isInteractive();
mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 38a44c6..7911c40 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -94,8 +94,6 @@
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mMyLooper));
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@@ -183,7 +181,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(context, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mMyLooper));
}
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 1ac0150..5fbf8de 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -92,8 +92,6 @@
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mMyLooper));
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@@ -140,7 +138,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(context, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mMyLooper));
}
};
@@ -168,6 +167,8 @@
mPlaybackLogicalAddress = mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress();
mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
mNativeWrapper.clearResultMessages();
+ mHdmiCecLocalDevicePlayback.mPlaybackDeviceActionOnRoutingControl =
+ HdmiProperties.playback_device_action_on_routing_control_values.NONE;
}
@Test
@@ -698,9 +699,12 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@Test
@@ -720,9 +724,12 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@Test
@@ -742,9 +749,12 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@Test
@@ -764,9 +774,12 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@Test
@@ -786,9 +799,12 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@Test
@@ -808,9 +824,37 @@
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSource);
+ }
+
+ @Test
+ public void handleOnStandby_CecMessageReceived() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV);
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
+ mHdmiCecLocalDevicePlayback.onStandby(true, HdmiControlService.STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSource);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 8ee983f..59711a6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -86,8 +86,6 @@
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mMyLooper));
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@@ -118,7 +116,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(context, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mMyLooper));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index 1c7ff42..572ffd9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -75,10 +75,12 @@
Context contextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
Looper myLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(contextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(myLooper));
- when(contextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(contextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(contextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(contextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(myLooper)));
+ when(contextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(contextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(myLooper)));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(contextSpy) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index b1d77d0..bcf30a2 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -192,10 +192,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, null);
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, null));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, null));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
index 826438f..4cd17e8 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -91,10 +91,12 @@
setHdmiControlEnabled(hdmiControlEnabled);
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(mContextSpy) {
@@ -120,7 +122,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
index 53b4b49..a9880c0 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -78,10 +78,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(mContextSpy,
@@ -108,7 +110,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
index 865eb7a..2cf4ef1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -78,10 +78,12 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
Looper myLooper = mTestLooper.getLooper();
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(myLooper));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
- when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(myLooper)));
+ when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+ new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(myLooper)));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(mContextSpy) {
@@ -107,7 +109,8 @@
@Override
protected PowerManager getPowerManager() {
- return powerManager;
+ return new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(myLooper));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
index 709b009..1b6bddc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
@@ -25,6 +25,7 @@
import android.util.LongSparseArray;
import com.android.internal.util.ArrayUtils;
+import com.android.server.utils.WatchedArrayMap;
import java.io.File;
import java.io.IOException;
@@ -33,7 +34,7 @@
public class KeySetManagerServiceTest extends AndroidTestCase {
- private ArrayMap<String, PackageSetting> mPackagesMap;
+ private WatchedArrayMap<String, PackageSetting> mPackagesMap;
private KeySetManagerService mKsms;
public PackageSetting generateFakePackageSetting(String name) {
@@ -46,7 +47,7 @@
@Override
public void setUp() throws Exception {
super.setUp();
- mPackagesMap = new ArrayMap<String, PackageSetting>();
+ mPackagesMap = new WatchedArrayMap<String, PackageSetting>();
mKsms = new KeySetManagerService(mPackagesMap);
}
@@ -94,7 +95,8 @@
}
public void testEncodePublicKey() throws IOException {
- ArrayMap<String, PackageSetting> packagesMap = new ArrayMap<String, PackageSetting>();
+ WatchedArrayMap<String, PackageSetting> packagesMap =
+ new WatchedArrayMap<String, PackageSetting>();
KeySetManagerService ksms = new KeySetManagerService(packagesMap);
PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index a231169..29f4aa9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -47,7 +47,6 @@
import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
@@ -64,6 +63,7 @@
import com.android.server.pm.permission.LegacyPermissionDataProvider;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.utils.WatchableTester;
+import com.android.server.utils.WatchedArrayMap;
import com.google.common.truth.Truth;
@@ -1202,9 +1202,8 @@
private void verifyKeySetMetaData(Settings settings)
throws ReflectiveOperationException, IllegalAccessException {
- ArrayMap<String, PackageSetting> packages =
- settings.mPackages.untrackedStorage();
- KeySetManagerService ksms = settings.mKeySetManagerService;
+ WatchedArrayMap<String, PackageSetting> packages = settings.mPackages;
+ KeySetManagerService ksms = settings.getKeySetManagerService();
/* verify keyset and public key ref counts */
assertThat(KeySetUtils.getKeySetRefCount(ksms, 1), is(2));
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
index 9001b3d..443476c 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
@@ -48,7 +48,7 @@
private static final float PRECISION = 0.001f;
private static final int GPS_MODE = 0; // LOCATION_MODE_NO_CHANGE
private static final int DEFAULT_GPS_MODE =
- PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
+ PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
private static final int SOUND_TRIGGER_MODE = 0; // SOUND_TRIGGER_MODE_ALL_ENABLED
private static final int DEFAULT_SOUND_TRIGGER_MODE =
PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY;
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/OWNERS b/services/tests/servicestests/src/com/android/server/power/batterysaver/OWNERS
new file mode 100644
index 0000000..08276f5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/power/batterysaver/OWNERS
\ No newline at end of file
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 ddaf3ab..9a89626 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2034,20 +2034,29 @@
.setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
.build();
- // Non-resizable
+ // Not allow non-resizable
mAtm.mForceResizableActivities = false;
+ mAtm.mSupportsNonResizableMultiWindow = -1;
mAtm.mDevEnableNonResizableMultiWindow = false;
assertFalse(activity.supportsSplitScreenWindowingMode());
// Force resizable
mAtm.mForceResizableActivities = true;
+ mAtm.mSupportsNonResizableMultiWindow = -1;
mAtm.mDevEnableNonResizableMultiWindow = false;
assertTrue(activity.supportsSplitScreenWindowingMode());
- // Allow non-resizable
+ // Use development option to allow non-resizable
mAtm.mForceResizableActivities = false;
+ mAtm.mSupportsNonResizableMultiWindow = -1;
mAtm.mDevEnableNonResizableMultiWindow = true;
assertTrue(activity.supportsSplitScreenWindowingMode());
+
+ // Always allow non-resizable
+ mAtm.mForceResizableActivities = false;
+ mAtm.mSupportsNonResizableMultiWindow = 1;
+ mAtm.mDevEnableNonResizableMultiWindow = false;
+ assertTrue(activity.supportsSplitScreenWindowingMode());
}
@Test
@@ -2058,20 +2067,29 @@
.setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
.build();
- // Non-resizable
+ // Not allow non-resizable
mAtm.mForceResizableActivities = false;
+ mAtm.mSupportsNonResizableMultiWindow = -1;
mAtm.mDevEnableNonResizableMultiWindow = false;
assertFalse(activity.supportsFreeform());
// Force resizable
mAtm.mForceResizableActivities = true;
+ mAtm.mSupportsNonResizableMultiWindow = -1;
mAtm.mDevEnableNonResizableMultiWindow = false;
assertTrue(activity.supportsFreeform());
- // Allow non-resizable
+ // Use development option to allow non-resizable
mAtm.mForceResizableActivities = false;
+ mAtm.mSupportsNonResizableMultiWindow = -1;
mAtm.mDevEnableNonResizableMultiWindow = true;
assertTrue(activity.supportsFreeform());
+
+ // Always allow non-resizable
+ mAtm.mForceResizableActivities = false;
+ mAtm.mSupportsNonResizableMultiWindow = 1;
+ mAtm.mDevEnableNonResizableMultiWindow = false;
+ assertTrue(activity.supportsFreeform());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 618de21..2558259 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -320,8 +320,8 @@
.build();
final Task task = activity.getTask();
- assertTrue(activity.supportsMultiWindow2());
- assertTrue(task.supportsMultiWindow2());
+ assertTrue(activity.supportsMultiWindow());
+ assertTrue(task.supportsMultiWindow());
}
@Test
@@ -336,14 +336,14 @@
// Device config as not support.
mAtm.mSupportsNonResizableMultiWindow = -1;
- assertFalse(activity.supportsMultiWindow2());
- assertFalse(task.supportsMultiWindow2());
+ assertFalse(activity.supportsMultiWindow());
+ assertFalse(task.supportsMultiWindow());
// Device config as always support.
mAtm.mSupportsNonResizableMultiWindow = 1;
- assertTrue(activity.supportsMultiWindow2());
- assertTrue(task.supportsMultiWindow2());
+ assertTrue(activity.supportsMultiWindow());
+ assertTrue(task.supportsMultiWindow());
// The default config is relying on the screen size.
mAtm.mSupportsNonResizableMultiWindow = 0;
@@ -351,14 +351,14 @@
// Supports on large screen.
tda.getConfiguration().smallestScreenWidthDp = mAtm.mLargeScreenSmallestScreenWidthDp;
- assertTrue(activity.supportsMultiWindow2());
- assertTrue(task.supportsMultiWindow2());
+ assertTrue(activity.supportsMultiWindow());
+ assertTrue(task.supportsMultiWindow());
// Not supports on small screen.
tda.getConfiguration().smallestScreenWidthDp = mAtm.mLargeScreenSmallestScreenWidthDp - 1;
- assertFalse(activity.supportsMultiWindow2());
- assertFalse(task.supportsMultiWindow2());
+ assertFalse(activity.supportsMultiWindow());
+ assertFalse(task.supportsMultiWindow());
}
@Test
@@ -381,14 +381,14 @@
// Ignore the activity min width/height for determine multi window eligibility.
mAtm.mRespectsActivityMinWidthHeightMultiWindow = -1;
- assertTrue(activity.supportsMultiWindow2());
- assertTrue(task.supportsMultiWindow2());
+ assertTrue(activity.supportsMultiWindow());
+ assertTrue(task.supportsMultiWindow());
// Always check the activity min width/height.
mAtm.mRespectsActivityMinWidthHeightMultiWindow = 1;
- assertFalse(activity.supportsMultiWindow2());
- assertFalse(task.supportsMultiWindow2());
+ assertFalse(activity.supportsMultiWindow());
+ assertFalse(task.supportsMultiWindow());
// The default config is relying on the screen size.
mAtm.mRespectsActivityMinWidthHeightMultiWindow = 0;
@@ -396,14 +396,14 @@
// Ignore on large screen.
tda.getConfiguration().smallestScreenWidthDp = mAtm.mLargeScreenSmallestScreenWidthDp;
- assertTrue(activity.supportsMultiWindow2());
- assertTrue(task.supportsMultiWindow2());
+ assertTrue(activity.supportsMultiWindow());
+ assertTrue(task.supportsMultiWindow());
// Check on small screen.
tda.getConfiguration().smallestScreenWidthDp = mAtm.mLargeScreenSmallestScreenWidthDp - 1;
- assertFalse(activity.supportsMultiWindow2());
- assertFalse(task.supportsMultiWindow2());
+ assertFalse(activity.supportsMultiWindow());
+ assertFalse(task.supportsMultiWindow());
}
@Test
@@ -429,14 +429,14 @@
// Always check the activity min width/height.
mAtm.mSupportsNonResizableMultiWindow = 1;
- assertTrue(activity.supportsMultiWindow2());
- assertTrue(task.supportsMultiWindow2());
+ assertTrue(activity.supportsMultiWindow());
+ assertTrue(task.supportsMultiWindow());
// The default config is relying on the screen size. Check for small screen
mAtm.mSupportsNonResizableMultiWindow = 0;
- assertTrue(activity.supportsMultiWindow2());
- assertTrue(task.supportsMultiWindow2());
+ assertTrue(activity.supportsMultiWindow());
+ assertTrue(task.supportsMultiWindow());
}
}
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 61b7002..42ef086 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -488,6 +488,7 @@
mLargeScreenSmallestScreenWidthDp = 600;
mSupportsNonResizableMultiWindow = 0;
mRespectsActivityMinWidthHeightMultiWindow = 0;
+ mForceResizableActivities = false;
doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
// allow background activity starts by default
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 8c87bef..3f1248a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -1193,13 +1193,13 @@
splitPrimaryRootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */);
// Can't reparent non-resizable to split screen
- mAtm.mDevEnableNonResizableMultiWindow = false;
+ mAtm.mSupportsNonResizableMultiWindow = -1;
mAtm.mWindowOrganizerController.applyTransaction(wct);
assertEquals(rootTask, activity.getRootTask());
// Allow reparent non-resizable to split screen
- mAtm.mDevEnableNonResizableMultiWindow = true;
+ mAtm.mSupportsNonResizableMultiWindow = 1;
mAtm.mWindowOrganizerController.applyTransaction(wct);
assertEquals(splitPrimaryRootTask, activity.getRootTask());
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index d1bd159..5df3d9c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -612,7 +612,7 @@
options,
new IDspHotwordDetectionCallback.Stub() {
@Override
- public void onRejected(@Nullable HotwordRejectedResult result)
+ public void onRejected(HotwordRejectedResult result)
throws RemoteException {
bestEffortClose(serviceAudioSink);
bestEffortClose(serviceAudioSource);
@@ -622,7 +622,7 @@
}
@Override
- public void onDetected(@Nullable HotwordDetectedResult triggerResult)
+ public void onDetected(HotwordDetectedResult triggerResult)
throws RemoteException {
bestEffortClose(serviceAudioSink);
bestEffortClose(serviceAudioSource);
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 73f1783..30403f4 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -806,6 +806,15 @@
*/
public static final String EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ =
"android.telecom.extra.AUDIO_CODEC_BANDWIDTH_KHZ";
+
+ /**
+ * Boolean connection extra key used to indicate whether device to device communication is
+ * available for the current call.
+ * @hide
+ */
+ public static final String EXTRA_IS_DEVICE_TO_DEVICE_COMMUNICATION_AVAILABLE =
+ "android.telecom.extra.IS_DEVICE_TO_DEVICE_COMMUNICATION_AVAILABLE";
+
/**
* Connection event used to inform Telecom that it should play the on hold tone. This is used
* to play a tone when the peer puts the current call on hold. Sent to Telecom via
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 1c8a1bf..9efdde4 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -341,7 +341,7 @@
private void testParcelSane(NetworkCapabilities cap) {
if (isAtLeastS()) {
- assertParcelSane(cap, 17);
+ assertParcelSane(cap, 16);
} else if (isAtLeastR()) {
assertParcelSane(cap, 15);
} else {
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index f277e94..8dbc6e6 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.CHANGE_NETWORK_STATE;
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.LOCAL_MAC_ADDRESS;
import static android.Manifest.permission.NETWORK_FACTORY;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
@@ -2777,8 +2778,9 @@
private void grantUsingBackgroundNetworksPermissionForUid(
final int uid, final String packageName) throws Exception {
- when(mPackageManager.getPackageInfo(eq(packageName), eq(GET_PERMISSIONS)))
- .thenReturn(buildPackageInfo(true, uid));
+ when(mPackageManager.getPackageInfo(
+ eq(packageName), eq(GET_PERMISSIONS | MATCH_ANY_USER)))
+ .thenReturn(buildPackageInfo(true /* hasSystemPermission */, uid));
mService.mPermissionMonitor.onPackageAdded(packageName, uid);
}
@@ -9469,9 +9471,9 @@
@Override
public TransportInfo makeCopy(@NetworkCapabilities.RedactionType long redactions) {
return new TestTransportInfo(
- (redactions & REDACT_FOR_ACCESS_FINE_LOCATION) != 0,
- (redactions & REDACT_FOR_LOCAL_MAC_ADDRESS) != 0,
- (redactions & REDACT_FOR_NETWORK_SETTINGS) != 0
+ locationRedacted | (redactions & REDACT_FOR_ACCESS_FINE_LOCATION) != 0,
+ localMacAddressRedacted | (redactions & REDACT_FOR_LOCAL_MAC_ADDRESS) != 0,
+ settingsRedacted | (redactions & REDACT_FOR_NETWORK_SETTINGS) != 0
);
}
@@ -9494,8 +9496,26 @@
public int hashCode() {
return Objects.hash(locationRedacted, localMacAddressRedacted, settingsRedacted);
}
+
+ @Override
+ public String toString() {
+ return String.format(
+ "TestTransportInfo{locationRedacted=%s macRedacted=%s settingsRedacted=%s}",
+ locationRedacted, localMacAddressRedacted, settingsRedacted);
+ }
}
+ private TestTransportInfo getTestTransportInfo(NetworkCapabilities nc) {
+ return (TestTransportInfo) nc.getTransportInfo();
+ }
+
+ private TestTransportInfo getTestTransportInfo(TestNetworkAgentWrapper n) {
+ final NetworkCapabilities nc = mCm.getNetworkCapabilities(n.getNetwork());
+ assertNotNull(nc);
+ return getTestTransportInfo(nc);
+ }
+
+
private void verifyNetworkCallbackLocationDataInclusionUsingTransportInfoAndOwnerUidInNetCaps(
@NonNull TestNetworkCallback wifiNetworkCallback, int actualOwnerUid,
@NonNull TransportInfo actualTransportInfo, int expectedOwnerUid,
@@ -9524,7 +9544,6 @@
wifiNetworkCallback.expectCapabilitiesThat(mWiFiNetworkAgent,
nc -> Objects.equals(expectedOwnerUid, nc.getOwnerUid())
&& Objects.equals(expectedTransportInfo, nc.getTransportInfo()));
-
}
@Test
@@ -9545,6 +9564,40 @@
wifiNetworkCallack, ownerUid, transportInfo, INVALID_UID, sanitizedTransportInfo);
}
+ @Test
+ public void testTransportInfoRedactionInSynchronousCalls() throws Exception {
+ final NetworkCapabilities ncTemplate = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_WIFI)
+ .setTransportInfo(new TestTransportInfo());
+
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, new LinkProperties(),
+ ncTemplate);
+ mWiFiNetworkAgent.connect(true /* validated; waits for callback */);
+
+ // NETWORK_SETTINGS redaction is controlled by the NETWORK_SETTINGS permission
+ assertTrue(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted);
+ withPermission(NETWORK_SETTINGS, () -> {
+ assertFalse(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted);
+ });
+ assertTrue(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted);
+
+ // LOCAL_MAC_ADDRESS redaction is controlled by the LOCAL_MAC_ADDRESS permission
+ assertTrue(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted);
+ withPermission(LOCAL_MAC_ADDRESS, () -> {
+ assertFalse(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted);
+ });
+ assertTrue(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted);
+
+ // Synchronous getNetworkCapabilities calls never return unredacted location-sensitive
+ // information.
+ assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted);
+ setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+ assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted);
+ denyAllLocationPrivilegedPermissions();
+ assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted);
+ }
+
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
throws Exception {
final Set<UidRange> vpnRange = Collections.singleton(PRIMARY_UIDRANGE);
@@ -9903,12 +9956,27 @@
// Connect the cell agent verify that it notifies TestNetworkCallback that it is available
final TestNetworkCallback callback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(callback);
- mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+
+ final NetworkCapabilities ncTemplate = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .setTransportInfo(new TestTransportInfo());
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, new LinkProperties(),
+ ncTemplate);
mCellNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
callback.assertNoCallback();
}
+ private boolean areConnDiagCapsRedacted(NetworkCapabilities nc) {
+ TestTransportInfo ti = (TestTransportInfo) nc.getTransportInfo();
+ return nc.getUids() == null
+ && nc.getAdministratorUids().length == 0
+ && nc.getOwnerUid() == Process.INVALID_UID
+ && getTestTransportInfo(nc).locationRedacted
+ && getTestTransportInfo(nc).localMacAddressRedacted
+ && getTestTransportInfo(nc).settingsRedacted;
+ }
+
@Test
public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable()
throws Exception {
@@ -9919,12 +9987,7 @@
// Verify onConnectivityReport fired
verify(mConnectivityDiagnosticsCallback).onConnectivityReportAvailable(
- argThat(report -> {
- final NetworkCapabilities nc = report.getNetworkCapabilities();
- return nc.getUids() == null
- && nc.getAdministratorUids().length == 0
- && nc.getOwnerUid() == Process.INVALID_UID;
- }));
+ argThat(report -> areConnDiagCapsRedacted(report.getNetworkCapabilities())));
}
@Test
@@ -9940,12 +10003,7 @@
// Verify onDataStallSuspected fired
verify(mConnectivityDiagnosticsCallback).onDataStallSuspected(
- argThat(report -> {
- final NetworkCapabilities nc = report.getNetworkCapabilities();
- return nc.getUids() == null
- && nc.getAdministratorUids().length == 0
- && nc.getOwnerUid() == Process.INVALID_UID;
- }));
+ argThat(report -> areConnDiagCapsRedacted(report.getNetworkCapabilities())));
}
@Test
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index d7535a9..02a5808 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -479,13 +479,14 @@
public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception {
when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
Arrays.asList(new PackageInfo[] {
- buildPackageInfo(/* SYSTEM */ true, SYSTEM_UID1, MOCK_USER1),
- buildPackageInfo(/* SYSTEM */ false, MOCK_UID1, MOCK_USER1),
- buildPackageInfo(/* SYSTEM */ false, MOCK_UID2, MOCK_USER1),
- buildPackageInfo(/* SYSTEM */ false, VPN_UID, MOCK_USER1)
+ buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1),
+ buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1),
+ buildPackageInfo(false /* hasSystemPermission */, MOCK_UID2, MOCK_USER1),
+ buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1)
}));
- when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS))).thenReturn(
- buildPackageInfo(false, MOCK_UID1, MOCK_USER1));
+ when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1),
+ eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
+ buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1));
mPermissionMonitor.startMonitoring();
// Every app on user 0 except MOCK_UID2 are under VPN.
final Set<UidRange> vpnRange1 = new HashSet<>(Arrays.asList(new UidRange[] {
@@ -530,11 +531,12 @@
public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception {
when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
Arrays.asList(new PackageInfo[] {
- buildPackageInfo(true, SYSTEM_UID1, MOCK_USER1),
- buildPackageInfo(false, VPN_UID, MOCK_USER1)
+ buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1),
+ buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1)
}));
- when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS))).thenReturn(
- buildPackageInfo(false, MOCK_UID1, MOCK_USER1));
+ when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1),
+ eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
+ buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1));
mPermissionMonitor.startMonitoring();
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(MOCK_USER1));
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 9410886..c59dcf8 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -16,13 +16,17 @@
package android.net.vcn;
+import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.net.NetworkCapabilities;
+import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeTunnelConnectionParams;
+import android.net.vcn.persistablebundleutils.IkeSessionParamsUtilsTest;
import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtilsTest;
import androidx.test.filters.SmallTest;
@@ -120,6 +124,21 @@
}
@Test
+ public void testBuilderRequiresMobikeEnabled() {
+ try {
+ final IkeSessionParams ikeParams =
+ IkeSessionParamsUtilsTest.createBuilderMinimum()
+ .removeIkeOption(IKE_OPTION_MOBIKE)
+ .build();
+ final IkeTunnelConnectionParams tunnelParams =
+ TunnelConnectionParamsUtilsTest.buildTestParams(ikeParams);
+ new VcnGatewayConnectionConfig.Builder(GATEWAY_CONNECTION_NAME_PREFIX, tunnelParams);
+ fail("Expected exception due to MOBIKE not enabled");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
public void testBuilderRequiresNonEmptyExposedCaps() {
try {
newBuilder()
diff --git a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
index 582275d..00a0bff 100644
--- a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
@@ -16,14 +16,17 @@
package android.net.vcn;
-import static android.net.NetworkCapabilities.REDACT_ALL;
+import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
+import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS;
import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
+import static android.net.NetworkCapabilities.REDACT_NONE;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
+import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.os.Parcel;
@@ -39,12 +42,6 @@
private static final VcnTransportInfo WIFI_UNDERLYING_INFO = new VcnTransportInfo(WIFI_INFO);
@Test
- public void testRedactionDefaults() {
- assertEquals(REDACT_ALL, CELL_UNDERLYING_INFO.getRedaction());
- assertEquals(REDACT_ALL, WIFI_UNDERLYING_INFO.getRedaction());
- }
-
- @Test
public void testGetWifiInfo() {
assertEquals(WIFI_INFO, WIFI_UNDERLYING_INFO.getWifiInfo());
@@ -59,15 +56,15 @@
}
@Test
- public void testMakeCopySetsRedactions() {
+ public void testMakeCopyRedactForAccessFineLocation() {
assertEquals(
- REDACT_FOR_NETWORK_SETTINGS,
- ((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_NETWORK_SETTINGS))
- .getRedaction());
+ SUB_ID,
+ ((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION))
+ .getSubId());
assertEquals(
- REDACT_FOR_NETWORK_SETTINGS,
- ((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy(REDACT_FOR_NETWORK_SETTINGS))
- .getRedaction());
+ WifiConfiguration.INVALID_NETWORK_ID,
+ ((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION))
+ .getWifiInfo().getNetworkId());
}
@Test
@@ -78,35 +75,31 @@
}
@Test
- public void testParcelUnparcel() {
- verifyParcelingIsNull(CELL_UNDERLYING_INFO);
- verifyParcelingIsNull(WIFI_UNDERLYING_INFO);
- }
-
- private void verifyParcelingIsNull(VcnTransportInfo vcnTransportInfo) {
- // Verify redacted by default
- Parcel parcel = Parcel.obtain();
- vcnTransportInfo.writeToParcel(parcel, 0 /* flags */);
- parcel.setDataPosition(0);
-
- assertNull(VcnTransportInfo.CREATOR.createFromParcel(parcel));
+ public void testApplicableRedactions() {
+ assertEquals(REDACT_NONE, CELL_UNDERLYING_INFO.getApplicableRedactions());
+ assertEquals(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS
+ | REDACT_FOR_NETWORK_SETTINGS,
+ WIFI_UNDERLYING_INFO.getApplicableRedactions());
}
@Test
- public void testParcelUnparcelNotRedactedForSysUi() {
- verifyParcelingForSysUi(CELL_UNDERLYING_INFO);
- verifyParcelingForSysUi(WIFI_UNDERLYING_INFO);
+ public void testParcelNotRedactedForSysUi() {
+ VcnTransportInfo cellRedacted = parcelForSysUi(CELL_UNDERLYING_INFO);
+ assertEquals(SUB_ID, cellRedacted.getSubId());
+ VcnTransportInfo wifiRedacted = parcelForSysUi(WIFI_UNDERLYING_INFO);
+ assertEquals(NETWORK_ID, wifiRedacted.getWifiInfo().getNetworkId());
}
- private void verifyParcelingForSysUi(VcnTransportInfo vcnTransportInfo) {
+ private VcnTransportInfo parcelForSysUi(VcnTransportInfo vcnTransportInfo) {
// Allow fully unredacted; SysUI will have all the relevant permissions.
- final VcnTransportInfo unRedacted = (VcnTransportInfo) vcnTransportInfo.makeCopy(0);
+ final VcnTransportInfo unRedacted = (VcnTransportInfo) vcnTransportInfo.makeCopy(
+ REDACT_NONE);
final Parcel parcel = Parcel.obtain();
unRedacted.writeToParcel(parcel, 0 /* flags */);
parcel.setDataPosition(0);
final VcnTransportInfo unparceled = VcnTransportInfo.CREATOR.createFromParcel(parcel);
assertEquals(vcnTransportInfo, unparceled);
- assertEquals(REDACT_ALL, unparceled.getRedaction());
+ return unparceled;
}
}
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
index 393787f..f385113 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
@@ -52,8 +52,8 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class IkeSessionParamsUtilsTest {
- // Package private for use in EncryptedTunnelParamsUtilsTest
- static IkeSessionParams.Builder createBuilderMinimum() {
+ // Public for use in VcnGatewayConnectionConfigTest, EncryptedTunnelParamsUtilsTest
+ public static IkeSessionParams.Builder createBuilderMinimum() {
final InetAddress serverAddress = InetAddresses.parseNumericAddress("192.0.2.100");
// TODO: b/185941731 Make sure all valid IKE_OPTIONS are added and validated.
@@ -63,6 +63,7 @@
.setLocalIdentification(new IkeFqdnIdentification("client.test.android.net"))
.setRemoteIdentification(new IkeFqdnIdentification("server.test.android.net"))
.addIkeOption(IkeSessionParams.IKE_OPTION_FORCE_PORT_4500)
+ .addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)
.setAuthPsk("psk".getBytes());
}
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java
index 0c8ad32..f9dc9eb 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
+import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeTunnelConnectionParams;
import androidx.test.filters.SmallTest;
@@ -31,9 +32,13 @@
public class TunnelConnectionParamsUtilsTest {
// Public for use in VcnGatewayConnectionConfigTest
public static IkeTunnelConnectionParams buildTestParams() {
+ return buildTestParams(IkeSessionParamsUtilsTest.createBuilderMinimum().build());
+ }
+
+ // Public for use in VcnGatewayConnectionConfigTest
+ public static IkeTunnelConnectionParams buildTestParams(IkeSessionParams params) {
return new IkeTunnelConnectionParams(
- IkeSessionParamsUtilsTest.createBuilderMinimum().build(),
- TunnelModeChildSessionParamsUtilsTest.createBuilderMinimum().build());
+ params, TunnelModeChildSessionParamsUtilsTest.createBuilderMinimum().build());
}
@Test
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 9ecd82f..3360d40 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -37,6 +37,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
@@ -66,6 +67,7 @@
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnConfigTest;
+import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.net.vcn.VcnManager;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
import android.os.IBinder;
@@ -197,7 +199,8 @@
.newVcnContext(
eq(mMockContext),
eq(mTestLooper.getLooper()),
- any(VcnNetworkProvider.class));
+ any(VcnNetworkProvider.class),
+ anyBoolean());
doReturn(mSubscriptionTracker)
.when(mMockDeps)
.newTelephonySubscriptionTracker(
@@ -371,6 +374,12 @@
TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
verify(mMockDeps)
+ .newVcnContext(
+ eq(mMockContext),
+ eq(mTestLooper.getLooper()),
+ any(VcnNetworkProvider.class),
+ anyBoolean());
+ verify(mMockDeps)
.newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot), any());
}
@@ -528,6 +537,28 @@
}
@Test
+ public void testSetVcnConfigTestModeRequiresPermission() throws Exception {
+ doThrow(new SecurityException("Requires MANAGE_TEST_NETWORKS"))
+ .when(mMockContext)
+ .enforceCallingPermission(
+ eq(android.Manifest.permission.MANAGE_TEST_NETWORKS), any());
+
+ final VcnConfig vcnConfig =
+ new VcnConfig.Builder(mMockContext)
+ .addGatewayConnectionConfig(
+ VcnGatewayConnectionConfigTest.buildTestConfig())
+ .setIsTestModeProfile()
+ .build();
+
+ try {
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, vcnConfig, TEST_PACKAGE_NAME);
+ fail("Expected exception due to using test-mode without permission");
+ } catch (SecurityException e) {
+ verify(mMockPolicyListener, never()).onPolicyChanged();
+ }
+ }
+
+ @Test
public void testSetVcnConfigNotifiesStatusCallback() throws Exception {
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2));
diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
index 8289e85..0b72cd9 100644
--- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -112,8 +113,14 @@
MockitoAnnotations.initMocks(this);
mTestLooper = new TestLooper();
- mVcnContext = spy(new VcnContext(mContext, mTestLooper.getLooper(), mVcnNetworkProvider));
- doNothing().when(mVcnContext).ensureRunningOnLooperThread();
+ mVcnContext =
+ spy(
+ new VcnContext(
+ mContext,
+ mTestLooper.getLooper(),
+ mVcnNetworkProvider,
+ false /* isInTestMode */));
+ resetVcnContext();
setupSystemService(
mContext,
@@ -132,6 +139,11 @@
mNetworkTrackerCb);
}
+ private void resetVcnContext() {
+ reset(mVcnContext);
+ doNothing().when(mVcnContext).ensureRunningOnLooperThread();
+ }
+
private static LinkProperties getLinkPropertiesWithName(String iface) {
LinkProperties linkProperties = new LinkProperties();
linkProperties.setInterfaceName(iface);
@@ -149,6 +161,31 @@
verifyNetworkRequestsRegistered(INITIAL_SUB_IDS);
}
+ @Test
+ public void testNetworkCallbacksRegisteredOnStartupForTestMode() {
+ final VcnContext vcnContext =
+ spy(
+ new VcnContext(
+ mContext,
+ mTestLooper.getLooper(),
+ mVcnNetworkProvider,
+ true /* isInTestMode */));
+
+ mUnderlyingNetworkTracker =
+ new UnderlyingNetworkTracker(
+ vcnContext,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET),
+ mNetworkTrackerCb);
+
+ verify(mConnectivityManager)
+ .requestBackgroundNetwork(
+ eq(getTestNetworkRequest(INITIAL_SUB_IDS)),
+ any(RouteSelectionCallback.class),
+ any());
+ }
+
private void verifyNetworkRequestsRegistered(Set<Integer> expectedSubIds) {
verify(mConnectivityManager)
.requestBackgroundNetwork(
@@ -165,7 +202,8 @@
verify(mConnectivityManager)
.requestBackgroundNetwork(
eq(getRouteSelectionRequest(expectedSubIds)),
- any(RouteSelectionCallback.class), any());
+ any(RouteSelectionCallback.class),
+ any());
}
@Test
@@ -204,6 +242,14 @@
return getExpectedRequestBase().setSubscriptionIds(netCapsSubIds).build();
}
+ private NetworkRequest getTestNetworkRequest(Set<Integer> netCapsSubIds) {
+ return new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ .setSubscriptionIds(netCapsSubIds)
+ .build();
+ }
+
private NetworkRequest.Builder getExpectedRequestBase() {
final NetworkRequest.Builder builder =
new NetworkRequest.Builder()
diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt
index 9ceb204..7cfa784 100644
--- a/tools/codegen/src/com/android/codegen/Utils.kt
+++ b/tools/codegen/src/com/android/codegen/Utils.kt
@@ -43,8 +43,8 @@
* cccc dd
*/
fun Iterable<Pair<String, String>>.columnize(separator: String = " | "): String {
- val col1w = map { (a, _) -> a.length }.maxOrNull()!!
- val col2w = map { (_, b) -> b.length }.maxOrNull()!!
+ val col1w = map { (a, _) -> a.length }.max()!!
+ val col2w = map { (_, b) -> b.length }.max()!!
return map { it.first.padEnd(col1w) + separator + it.second.padEnd(col2w) }.joinToString("\n")
}