diff options
105 files changed, 2858 insertions, 1220 deletions
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 4fbe232556ed..df9f2a3cbb25 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -57,6 +57,7 @@ interface INotificationManager @UnsupportedAppUsage void cancelNotificationWithTag(String pkg, String opPkg, String tag, int id, int userId); + boolean isInCall(String pkg, int uid); void setShowBadge(String pkg, int uid, boolean showBadge); boolean canShowBadge(String pkg, int uid); boolean hasSentValidMsg(String pkg, int uid); diff --git a/core/java/android/app/admin/DevicePolicyResourcesManager.java b/core/java/android/app/admin/DevicePolicyResourcesManager.java index 06729222dea1..e8eb792f7fd7 100644 --- a/core/java/android/app/admin/DevicePolicyResourcesManager.java +++ b/core/java/android/app/admin/DevicePolicyResourcesManager.java @@ -70,6 +70,7 @@ public class DevicePolicyResourcesManager { * * <p>Important notes to consider when using this API: * <ul> + * <li> Updated resources are persisted over reboots. * <li>{@link #getDrawable} references the resource * {@link DevicePolicyDrawableResource#getResourceIdInCallingPackage()} in the * calling package each time it gets called. You have to ensure that the resource is always @@ -381,7 +382,9 @@ public class DevicePolicyResourcesManager { * * <p>Important notes to consider when using this API: * <ul> - * <li> {@link #getString} references the resource {@code callingPackageResourceId} in the + * <li> Updated resources are persisted over reboots. + * <li> {@link #getString} references the resource + * {@link DevicePolicyStringResource#getResourceIdInCallingPackage()} in the * calling package each time it gets called. You have to ensure that the resource is always * available in the calling package as long as it is used as an updated resource. * <li> You still have to re-call {@code setStrings} even if you only make changes to the diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java index 69e63133eb03..2d1a3fe8e967 100644 --- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java @@ -57,9 +57,11 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork @NonNull private final Set<Integer> mAllowedSpecificCarrierIds; private static final String ROAMING_MATCH_KEY = "mRoamingMatchCriteria"; + private static final int DEFAULT_ROAMING_MATCH_CRITERIA = MATCH_ANY; private final int mRoamingMatchCriteria; private static final String OPPORTUNISTIC_MATCH_KEY = "mOpportunisticMatchCriteria"; + private static final int DEFAULT_OPPORTUNISTIC_MATCH_CRITERIA = MATCH_ANY; private final int mOpportunisticMatchCriteria; private VcnCellUnderlyingNetworkTemplate( @@ -253,23 +255,31 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork /** @hide */ @Override void dumpTransportSpecificFields(IndentingPrintWriter pw) { - pw.println("mAllowedNetworkPlmnIds: " + mAllowedNetworkPlmnIds.toString()); - pw.println("mAllowedSpecificCarrierIds: " + mAllowedSpecificCarrierIds.toString()); - pw.println("mRoamingMatchCriteria: " + getMatchCriteriaString(mRoamingMatchCriteria)); - pw.println( - "mOpportunisticMatchCriteria: " - + getMatchCriteriaString(mOpportunisticMatchCriteria)); + if (!mAllowedNetworkPlmnIds.isEmpty()) { + pw.println("mAllowedNetworkPlmnIds: " + mAllowedNetworkPlmnIds); + } + if (!mAllowedNetworkPlmnIds.isEmpty()) { + pw.println("mAllowedSpecificCarrierIds: " + mAllowedSpecificCarrierIds); + } + if (mRoamingMatchCriteria != DEFAULT_ROAMING_MATCH_CRITERIA) { + pw.println("mRoamingMatchCriteria: " + getMatchCriteriaString(mRoamingMatchCriteria)); + } + if (mOpportunisticMatchCriteria != DEFAULT_OPPORTUNISTIC_MATCH_CRITERIA) { + pw.println( + "mOpportunisticMatchCriteria: " + + getMatchCriteriaString(mOpportunisticMatchCriteria)); + } } /** This class is used to incrementally build VcnCellUnderlyingNetworkTemplate objects. */ public static final class Builder { - private int mMeteredMatchCriteria = MATCH_ANY; + private int mMeteredMatchCriteria = DEFAULT_METERED_MATCH_CRITERIA; @NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>(); @NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>(); - private int mRoamingMatchCriteria = MATCH_ANY; - private int mOpportunisticMatchCriteria = MATCH_ANY; + private int mRoamingMatchCriteria = DEFAULT_ROAMING_MATCH_CRITERIA; + private int mOpportunisticMatchCriteria = DEFAULT_OPPORTUNISTIC_MATCH_CRITERIA; private int mMinEntryUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS; private int mMinExitUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS; diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java index 3a9ca3edded7..9235d0913295 100644 --- a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java @@ -15,8 +15,6 @@ */ package android.net.vcn; -import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY; - import static com.android.internal.annotations.VisibleForTesting.Visibility; import android.annotation.IntDef; @@ -88,6 +86,9 @@ public abstract class VcnUnderlyingNetworkTemplate { /** @hide */ static final String METERED_MATCH_KEY = "mMeteredMatchCriteria"; + /** @hide */ + static final int DEFAULT_METERED_MATCH_CRITERIA = MATCH_ANY; + private final int mMeteredMatchCriteria; /** @hide */ @@ -237,11 +238,21 @@ public abstract class VcnUnderlyingNetworkTemplate { pw.println(this.getClass().getSimpleName() + ":"); pw.increaseIndent(); - pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria)); - pw.println("mMinEntryUpstreamBandwidthKbps: " + mMinEntryUpstreamBandwidthKbps); - pw.println("mMinExitUpstreamBandwidthKbps: " + mMinExitUpstreamBandwidthKbps); - pw.println("mMinEntryDownstreamBandwidthKbps: " + mMinEntryDownstreamBandwidthKbps); - pw.println("mMinExitDownstreamBandwidthKbps: " + mMinExitDownstreamBandwidthKbps); + if (mMeteredMatchCriteria != DEFAULT_METERED_MATCH_CRITERIA) { + pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria)); + } + if (mMinEntryUpstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) { + pw.println("mMinEntryUpstreamBandwidthKbps: " + mMinEntryUpstreamBandwidthKbps); + } + if (mMinExitUpstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) { + pw.println("mMinExitUpstreamBandwidthKbps: " + mMinExitUpstreamBandwidthKbps); + } + if (mMinEntryDownstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) { + pw.println("mMinEntryDownstreamBandwidthKbps: " + mMinEntryDownstreamBandwidthKbps); + } + if (mMinExitDownstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) { + pw.println("mMinExitDownstreamBandwidthKbps: " + mMinExitDownstreamBandwidthKbps); + } dumpTransportSpecificFields(pw); pw.decreaseIndent(); diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java index 23a07abdf0cb..2544a6d63561 100644 --- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java @@ -147,7 +147,9 @@ public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetwork /** @hide */ @Override void dumpTransportSpecificFields(IndentingPrintWriter pw) { - pw.println("mSsids: " + mSsids); + if (!mSsids.isEmpty()) { + pw.println("mSsids: " + mSsids); + } } /** diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 8e9f9d9fb4f3..cfe44bbbf3c6 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -3590,12 +3590,13 @@ public interface WindowManager extends ViewManager { /** * If specified, the insets provided by this window will be our window frame minus the - * insets specified by providedInternalInsets. This should not be used together with - * {@link WindowState#mGivenContentInsets}. If both of them are set, both will be applied. + * insets specified by providedInternalInsets for each type. This should not be used + * together with {@link WindowState#mGivenContentInsets}. If both of them are set, both will + * be applied. * * @hide */ - public Insets providedInternalInsets = Insets.NONE; + public Insets[] providedInternalInsets; /** * If specified, the insets provided by this window for the IME will be our window frame @@ -3603,7 +3604,7 @@ public interface WindowManager extends ViewManager { * * @hide */ - public Insets providedInternalImeInsets = Insets.NONE; + public Insets[] providedInternalImeInsets; /** * If specified, the frame that used to calculate relative {@link RoundedCorner} will be @@ -3989,8 +3990,18 @@ public interface WindowManager extends ViewManager { } else { out.writeInt(0); } - providedInternalInsets.writeToParcel(out, 0 /* parcelableFlags */); - providedInternalImeInsets.writeToParcel(out, 0 /* parcelableFlags */); + if (providedInternalInsets != null) { + out.writeInt(providedInternalInsets.length); + out.writeTypedArray(providedInternalInsets, 0 /* parcelableFlags */); + } else { + out.writeInt(0); + } + if (providedInternalImeInsets != null) { + out.writeInt(providedInternalImeInsets.length); + out.writeTypedArray(providedInternalImeInsets, 0 /* parcelableFlags */); + } else { + out.writeInt(0); + } out.writeBoolean(insetsRoundedCornerFrame); if (paramsForRotation != null) { checkNonRecursiveParams(); @@ -4070,8 +4081,16 @@ public interface WindowManager extends ViewManager { providesInsetsTypes = new int[insetsTypesLength]; in.readIntArray(providesInsetsTypes); } - providedInternalInsets = Insets.CREATOR.createFromParcel(in); - providedInternalImeInsets = Insets.CREATOR.createFromParcel(in); + int providedInternalInsetsLength = in.readInt(); + if (providedInternalInsetsLength > 0) { + providedInternalInsets = new Insets[providedInternalInsetsLength]; + in.readTypedArray(providedInternalInsets, Insets.CREATOR); + } + int providedInternalImeInsetsLength = in.readInt(); + if (providedInternalImeInsetsLength > 0) { + providedInternalImeInsets = new Insets[providedInternalImeInsetsLength]; + in.readTypedArray(providedInternalImeInsets, Insets.CREATOR); + } insetsRoundedCornerFrame = in.readBoolean(); int paramsForRotationLength = in.readInt(); if (paramsForRotationLength > 0) { @@ -4374,12 +4393,12 @@ public interface WindowManager extends ViewManager { changes |= LAYOUT_CHANGED; } - if (!providedInternalInsets.equals(o.providedInternalInsets)) { + if (!Arrays.equals(providedInternalInsets, o.providedInternalInsets)) { providedInternalInsets = o.providedInternalInsets; changes |= LAYOUT_CHANGED; } - if (!providedInternalImeInsets.equals(o.providedInternalImeInsets)) { + if (!Arrays.equals(providedInternalImeInsets, o.providedInternalImeInsets)) { providedInternalImeInsets = o.providedInternalImeInsets; changes |= LAYOUT_CHANGED; } @@ -4590,13 +4609,21 @@ public interface WindowManager extends ViewManager { sb.append(InsetsState.typeToString(providesInsetsTypes[i])); } } - if (!providedInternalInsets.equals(Insets.NONE)) { + if (providedInternalInsets != null) { + sb.append(System.lineSeparator()); sb.append(" providedInternalInsets="); - sb.append(providedInternalInsets); + for (int i = 0; i < providedInternalInsets.length; ++i) { + if (i > 0) sb.append(' '); + sb.append((providedInternalInsets[i])); + } } - if (!providedInternalImeInsets.equals(Insets.NONE)) { + if (providedInternalImeInsets != null) { + sb.append(System.lineSeparator()); sb.append(" providedInternalImeInsets="); - sb.append(providedInternalImeInsets); + for (int i = 0; i < providedInternalImeInsets.length; ++i) { + if (i > 0) sb.append(' '); + sb.append((providedInternalImeInsets[i])); + } } if (insetsRoundedCornerFrame) { sb.append(" insetsRoundedCornerFrame="); diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java index fadeea41c833..314b0a0c81db 100644 --- a/core/java/com/android/internal/app/LocalePickerWithRegion.java +++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java @@ -176,8 +176,9 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O // Add current system language into suggestion list for(LocaleStore.LocaleInfo localeInfo: LocaleStore.getSystemCurrentLocaleInfo()) { - if (appCurrentLocale == null || - !localeInfo.getLocale().equals(appCurrentLocale.getLocale())) { + boolean isNotCurrentLocale = appCurrentLocale == null + || !localeInfo.getLocale().equals(appCurrentLocale.getLocale()); + if (!isForCountryMode && isNotCurrentLocale) { mLocaleList.add(localeInfo); } } diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java index 68b8968fe399..18fde4794969 100644 --- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java +++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java @@ -217,22 +217,18 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { break; case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER: if (!(convertView instanceof ViewGroup)) { + TextView title; if (((LocaleStore.LocaleInfo)getItem(position)).isAppCurrentLocale()) { convertView = mInflater.inflate( - R.layout.app_language_picker_system_current, parent, false); + R.layout.app_language_picker_current_locale_item, parent, false); + title = convertView.findViewById(R.id.language_picker_item); } else { convertView = mInflater.inflate( - R.layout.app_language_picker_system_default, parent, false); + R.layout.language_picker_item, parent, false); + title = convertView.findViewById(R.id.locale); } + title.setText(R.string.system_locale_title); } - - Locale defaultLocale = Locale.getDefault(); - TextView title = convertView.findViewById(R.id.locale); - title.setText(R.string.system_locale_title); - title.setTextLocale(defaultLocale); - TextView subtitle = convertView.findViewById(R.id.system_locale_subtitle); - subtitle.setText(defaultLocale.getDisplayName()); - subtitle.setTextLocale(defaultLocale); break; case TYPE_CURRENT_LOCALE: if (!(convertView instanceof ViewGroup)) { diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 03187602b407..9b09616d4630 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -703,12 +703,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "-1427392850": { - "message": "WindowState: Setting back callback %s (priority: %d) (Client IWindow: %s). (WindowState: %s)", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/WindowState.java" - }, "-1427184084": { "message": "addWindow: New client %s: window=%s Callers=%s", "level": "VERBOSE", @@ -859,6 +853,12 @@ "group": "WM_DEBUG_ANIM", "at": "com\/android\/server\/wm\/WindowState.java" }, + "-1277068810": { + "message": "startBackNavigation currentTask=%s, topRunningActivity=%s, callbackInfo=%s, currentFocus=%s", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "-1270731689": { "message": "Attempted to set replacing window on app token with no content %s", "level": "WARN", @@ -1099,12 +1099,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "-1010850753": { - "message": "No focused window, defaulting to top task's window", - "level": "WARN", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, "-1009117329": { "message": "isFetchingAppTransitionSpecs=true", "level": "VERBOSE", @@ -3043,12 +3037,6 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, - "878005951": { - "message": "startBackNavigation task=%s, topRunningActivity=%s, callbackInfo=%s, currentFocus=%s", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, "892244061": { "message": "Waiting for drawn %s: removed=%b visible=%b mHasSurface=%b drawState=%d", "level": "INFO", @@ -3331,12 +3319,6 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/DisplayContent.java" }, - "1172542963": { - "message": "onBackNavigationDone backType=%s, task=%s, prevTaskTopActivity=%s", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, "1175495463": { "message": "ImeContainer just became organized. Reparenting under parent. imeParentSurfaceControl=%s", "level": "INFO", @@ -3409,6 +3391,12 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "1264179654": { + "message": "No focused window, defaulting to top current task's window", + "level": "WARN", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "1270792394": { "message": "Resumed after relaunch %s", "level": "DEBUG", @@ -3859,6 +3847,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1778919449": { + "message": "onBackNavigationDone backType=%s, task=%s, prevActivity=%s", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "1781673113": { "message": "onAnimationFinished(): targetRootTask=%s targetActivity=%s mRestoreTargetBehindRootTask=%s", "level": "DEBUG", diff --git a/data/keyboards/Vendor_0e6f_Product_f501.kl b/data/keyboards/Vendor_0e6f_Product_f501.kl new file mode 100644 index 000000000000..b46c005353b1 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_f501.kl @@ -0,0 +1,55 @@ +# Copyright (C) 2022 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. + +# +# XBox-compatible USB Controller +# + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt + +# Button labeled as "BACK" (left-pointing triangle) +key 314 BUTTON_SELECT + +# The branded "X" button in the center of the controller +key 316 BUTTON_MODE + +# Button labeled as "START" (right-pointing triangle) +key 315 BUTTON_START diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 54bab4ad3780..ffd041f60e26 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -1637,8 +1637,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu * Sets whether the keystore requires the screen to be unlocked before allowing decryption * using this key. If this is set to {@code true}, any attempt to decrypt or sign using this * key while the screen is locked will fail. A locked device requires a PIN, password, - * biometric, or other trusted factor to access. While the screen is locked, the key can - * still be used for encryption or signature verification. + * biometric, or other trusted factor to access. While the screen is locked, any associated + * public key can still be used (e.g for signature verification). */ @NonNull public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) { diff --git a/libs/WindowManager/Jetpack/src/TEST_MAPPING b/libs/WindowManager/Jetpack/src/TEST_MAPPING new file mode 100644 index 000000000000..eacfe2520a6a --- /dev/null +++ b/libs/WindowManager/Jetpack/src/TEST_MAPPING @@ -0,0 +1,32 @@ +{ + "presubmit": [ + { + "name": "WMJetpackUnitTests", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + } + ] + }, + { + "name": "CtsWindowManagerJetpackTestCases", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + } + ] + } + ] +}
\ No newline at end of file diff --git a/libs/WindowManager/Jetpack/tests/unittest/Android.bp b/libs/WindowManager/Jetpack/tests/unittest/Android.bp index 212fbd0a6752..b6e743a2b7e1 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/Android.bp +++ b/libs/WindowManager/Jetpack/tests/unittest/Android.bp @@ -23,6 +23,8 @@ package { android_test { name: "WMJetpackUnitTests", + // To make the test run via TEST_MAPPING + test_suites: ["device-tests"], srcs: [ "**/*.java", diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java index b6df876e1956..13a2c78d463e 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java @@ -18,6 +18,8 @@ package androidx.window.extensions; import static com.google.common.truth.Truth.assertThat; +import android.platform.test.annotations.Presubmit; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -25,6 +27,13 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +/** + * Test class for {@link WindowExtensionsTest}. + * + * Build/Install/Run: + * atest WMJetpackUnitTests:WindowExtensionsTest + */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class WindowExtensionsTest { diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java index 26463c18b9a0..b06ce4c19d5c 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java @@ -24,6 +24,8 @@ import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.never; +import android.platform.test.annotations.Presubmit; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -39,6 +41,7 @@ import org.mockito.MockitoAnnotations; * Build/Install/Run: * atest WMJetpackUnitTests:JetpackTaskFragmentOrganizerTest */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class JetpackTaskFragmentOrganizerTest { diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 120c7ebfe2da..a26a4b657d68 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -23,6 +23,8 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import android.platform.test.annotations.Presubmit; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.window.extensions.embedding.SplitController.TaskContainer; @@ -37,6 +39,7 @@ import org.junit.runner.RunWith; * Build/Install/Run: * atest WMJetpackUnitTests:SplitController */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class SplitControllerTest { diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java index 7f88f4e7ff2e..af3ad70c04db 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java @@ -21,6 +21,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.never; +import android.platform.test.annotations.Presubmit; import android.window.TaskFragmentOrganizer; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -38,6 +39,7 @@ import org.mockito.MockitoAnnotations; * Build/Install/Run: * atest WMJetpackUnitTests:TaskFragmentAnimationControllerTest */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class TaskFragmentAnimationControllerTest { diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp index ab00dd5a487c..dc72aead4873 100644 --- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp @@ -61,6 +61,17 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { return; } + // canvas may be an AlphaFilterCanvas, which is intended to draw with a + // modified alpha. We do not have a way to do this without drawing into an + // extra layer, which would have a performance cost. Draw directly into the + // underlying gpu canvas. This matches prior behavior and the behavior in + // Vulkan. + { + auto* gpuCanvas = SkAndroidFrameworkUtils::getBaseWrappedCanvas(canvas); + LOG_ALWAYS_FATAL_IF(!gpuCanvas, "GLFunctorDrawable::onDraw is using an invalid canvas!"); + canvas = gpuCanvas; + } + // flush will create a GrRenderTarget if not already present. canvas->flush(); diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java index eec73ff37775..72383fe59e7e 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java @@ -122,15 +122,66 @@ public class CollapsingCoordinatorLayout extends CoordinatorLayout { } if (activity instanceof AppCompatActivity) { - initSupportToolbar((AppCompatActivity) activity); + initSettingsStyleToolBar((SupportActionBarHost) + toolBar -> { + AppCompatActivity appCompatActivity = (AppCompatActivity) activity; + appCompatActivity.setSupportActionBar(toolBar); + return appCompatActivity.getSupportActionBar(); + }); + } else { + initSettingsStyleToolBar((ActionBarHost) + toolBar -> { + activity.setActionBar(toolBar); + return activity.getActionBar(); + }); + } + } + + /** + * Initialize some attributes of {@link ActionBar}. + * + * @param actionBarHost Host Activity that is not AppCompat. + */ + public void initSettingsStyleToolBar(ActionBarHost actionBarHost) { + if (actionBarHost == null) { + Log.w(TAG, "initSettingsStyleToolBar: actionBarHost is null"); return; } final Toolbar toolbar = findViewById(R.id.action_bar); - activity.setActionBar(toolbar); + final ActionBar actionBar = actionBarHost.setupActionBar(toolbar); + + // Enable title and home button by default + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setHomeButtonEnabled(true); + actionBar.setDisplayShowTitleEnabled(true); + } + } + + /** + * Initialize some attributes of {@link ActionBar}. + * + * @param supportActionBarHost Host Activity that is AppCompat. + */ + public void initSettingsStyleToolBar(SupportActionBarHost supportActionBarHost) { + if (supportActionBarHost == null) { + Log.w(TAG, "initSettingsStyleToolBar: supportActionBarHost is null"); + return; + } + if (mCollapsingToolbarLayout == null) { + return; + } + + mCollapsingToolbarLayout.removeAllViews(); + inflate(getContext(), R.layout.support_toolbar, mCollapsingToolbarLayout); + final androidx.appcompat.widget.Toolbar supportToolbar = + mCollapsingToolbarLayout.findViewById(R.id.support_action_bar); + + final androidx.appcompat.app.ActionBar actionBar = + supportActionBarHost.setupSupportActionBar(supportToolbar); // Enable title and home button by default - final ActionBar actionBar = activity.getActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeButtonEnabled(true); @@ -156,20 +207,27 @@ public class CollapsingCoordinatorLayout extends CoordinatorLayout { } } - /** - * Returns an instance of collapsing toolbar. - */ + /** Returns an instance of collapsing toolbar. */ public CollapsingToolbarLayout getCollapsingToolbarLayout() { return mCollapsingToolbarLayout; } - /** - * Return an instance of app bar. - */ + /** Return an instance of app bar. */ public AppBarLayout getAppBarLayout() { return mAppBarLayout; } + /** Returns the content frame layout. */ + public View getContentFrameLayout() { + return findViewById(R.id.content_frame); + } + + /** Returns the AppCompat Toolbar. */ + public androidx.appcompat.widget.Toolbar getSupportToolbar() { + return (androidx.appcompat.widget.Toolbar) + mCollapsingToolbarLayout.findViewById(R.id.support_action_bar); + } + private void disableCollapsingToolbarLayoutScrollingBehavior() { if (mAppBarLayout == null) { return; @@ -187,25 +245,22 @@ public class CollapsingCoordinatorLayout extends CoordinatorLayout { params.setBehavior(behavior); } - // This API is for supportActionBar of {@link AppCompatActivity} - private void initSupportToolbar(AppCompatActivity appCompatActivity) { - if (mCollapsingToolbarLayout == null) { - return; - } - - mCollapsingToolbarLayout.removeAllViews(); - inflate(getContext(), R.layout.support_toolbar, mCollapsingToolbarLayout); - final androidx.appcompat.widget.Toolbar supportToolbar = - mCollapsingToolbarLayout.findViewById(R.id.support_action_bar); - - appCompatActivity.setSupportActionBar(supportToolbar); + /** Interface to be implemented by a host Activity that is not AppCompat. */ + public interface ActionBarHost { + /** + * Sets a Toolbar as an actionBar and optionally returns an ActionBar represented by + * this toolbar if it should be used. + */ + @Nullable ActionBar setupActionBar(Toolbar toolbar); + } - // Enable title and home button by default - final androidx.appcompat.app.ActionBar actionBar = appCompatActivity.getSupportActionBar(); - if (actionBar != null) { - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setHomeButtonEnabled(true); - actionBar.setDisplayShowTitleEnabled(true); - } + /** Interface to be implemented by a host Activity that is AppCompat. */ + public interface SupportActionBarHost { + /** + * Sets a Toolbar as an actionBar and optionally returns an ActionBar represented by + * this toolbar if it should be used. + */ + @Nullable androidx.appcompat.app.ActionBar setupSupportActionBar( + androidx.appcompat.widget.Toolbar toolbar); } } diff --git a/packages/SettingsLib/SettingsSpinner/res/layout-v31/settings_spinner_dropdown_view.xml b/packages/SettingsLib/SettingsSpinner/res/layout-v33/settings_spinner_dropdown_view.xml index cea1133f7fee..cea1133f7fee 100644 --- a/packages/SettingsLib/SettingsSpinner/res/layout-v31/settings_spinner_dropdown_view.xml +++ b/packages/SettingsLib/SettingsSpinner/res/layout-v33/settings_spinner_dropdown_view.xml diff --git a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_view.xml b/packages/SettingsLib/SettingsSpinner/res/layout-v33/settings_spinner_view.xml index 75de34e86bc4..1d0c9b941881 100644 --- a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_view.xml +++ b/packages/SettingsLib/SettingsSpinner/res/layout-v33/settings_spinner_view.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2018 The Android Open Source Project + Copyright (C) 2022 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. diff --git a/packages/SettingsLib/SettingsSpinner/res/values/dimens.xml b/packages/SettingsLib/SettingsSpinner/res/values-v33/dimens.xml index d526df6bedd4..10aa84888d4e 100644 --- a/packages/SettingsLib/SettingsSpinner/res/values/dimens.xml +++ b/packages/SettingsLib/SettingsSpinner/res/values-v33/dimens.xml @@ -16,5 +16,6 @@ <resources> <dimen name="spinner_height">36dp</dimen> + <dimen name="spinner_dropdown_height">48dp</dimen> <dimen name="spinner_padding_top_or_bottom">8dp</dimen> </resources> diff --git a/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values-v33/styles.xml index fd45a16f24ba..6e26ae180685 100644 --- a/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml +++ b/packages/SettingsLib/SettingsSpinner/res/values-v33/styles.xml @@ -35,7 +35,7 @@ <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item> <item name="android:maxLines">1</item> <item name="android:ellipsize">marquee</item> - <item name="android:minHeight">@dimen/spinner_height</item> + <item name="android:minHeight">@dimen/spinner_dropdown_height</item> <item name="android:paddingStart">16dp</item> <item name="android:paddingEnd">36dp</item> <item name="android:paddingTop">@dimen/spinner_padding_top_or_bottom</item> diff --git a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values/styles.xml deleted file mode 100644 index 8ea1f9a794bc..000000000000 --- a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2018 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. - --> - -<resources> - <style name="SettingsSpinnerTitleBar"> - <item name="android:textAppearance">?android:attr/textAppearanceButton</item> - <item name="android:maxLines">1</item> - <item name="android:ellipsize">marquee</item> - <item name="android:paddingStart">16dp</item> - <item name="android:paddingEnd">36dp</item> - <item name="android:paddingTop">@dimen/spinner_padding_top_or_bottom</item> - <item name="android:paddingBottom">@dimen/spinner_padding_top_or_bottom</item> - </style> -</resources> diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java index 26112074a130..7288494beb21 100644 --- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java +++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java @@ -41,7 +41,7 @@ public class SettingsSpinnerAdapter<T> extends ArrayAdapter<T> { * access the current theme, resources, etc. */ public SettingsSpinnerAdapter(Context context) { - super(context, DEFAULT_RESOURCE); + super(context, getDefaultResource()); setDropDownViewResource(getDropdownResource()); mDefaultInflater = LayoutInflater.from(context); @@ -51,7 +51,7 @@ public class SettingsSpinnerAdapter<T> extends ArrayAdapter<T> { * In overridded {@link #getView(int, View, ViewGroup)}, use this method to get default view. */ public View getDefaultView(int position, View convertView, ViewGroup parent) { - return mDefaultInflater.inflate(DEFAULT_RESOURCE, parent, false /* attachToRoot */); + return mDefaultInflater.inflate(getDefaultResource(), parent, false /* attachToRoot */); } /** @@ -62,8 +62,12 @@ public class SettingsSpinnerAdapter<T> extends ArrayAdapter<T> { return mDefaultInflater.inflate(getDropdownResource(), parent, false /* attachToRoot */); } - private int getDropdownResource() { - return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + private static int getDefaultResource() { + return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + ? DEFAULT_RESOURCE : android.R.layout.simple_spinner_dropdown_item; + } + private static int getDropdownResource() { + return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) ? DFAULT_DROPDOWN_RESOURCE : android.R.layout.simple_spinner_dropdown_item; } } diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml deleted file mode 100644 index e1764afe5d91..000000000000 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2022 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. - --> - -<ripple -xmlns:android="http://schemas.android.com/apk/res/android" -android:color="@color/settingslib_ripple_color"> - -<item android:id="@android:id/background"> - <layer-list android:paddingMode="stack"> - <item - android:top="8dp" - android:bottom="8dp"> - - <shape> - <corners android:radius="28dp"/> - <solid android:color="@android:color/system_accent1_100"/> - <size android:height="@dimen/settingslib_spinner_height"/> - </shape> - </item> - - <item - android:gravity="center|end" - android:width="18dp" - android:height="18dp" - android:end="12dp" - android:drawable="@drawable/settingslib_arrow_drop_down"/> - </layer-list> -</item> -</ripple> diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_arrow_drop_down.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_arrow_drop_down.xml index 770a69d3e8e6..770a69d3e8e6 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_arrow_drop_down.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_arrow_drop_down.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_spinner_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_background.xml index 746671254afd..fbda83272ddd 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_spinner_background.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_background.xml @@ -16,33 +16,30 @@ --> <ripple -xmlns:android="http://schemas.android.com/apk/res/android" -android:color="@color/settingslib_ripple_color"> + xmlns:android="http://schemas.android.com/apk/res/android" + android:color="@color/settingslib_ripple_color"> <item android:id="@android:id/background"> - <layer-list android:paddingMode="stack"> + <layer-list + android:paddingMode="stack" + android:paddingStart="0dp" + android:paddingEnd="24dp"> <item android:top="8dp" android:bottom="8dp"> <shape> - <corners - android:radius="20dp"/> - <solid - android:color="?android:attr/colorPrimary"/> - <stroke - android:color="#1f000000" - android:width="1dp"/> - <size - android:height="32dp"/> + <corners android:radius="28dp"/> + <solid android:color="@android:color/system_accent1_100"/> + <size android:height="@dimen/settingslib_spinner_height"/> </shape> </item> <item android:gravity="center|end" - android:width="24dp" - android:height="24dp" - android:end="4dp" + android:width="18dp" + android:height="18dp" + android:end="12dp" android:drawable="@drawable/settingslib_arrow_drop_down"/> </layer-list> </item> diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_dropdown_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_dropdown_background.xml index 056fb828baad..50ef8fb50f19 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_dropdown_background.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_dropdown_background.xml @@ -20,9 +20,17 @@ android:color="@color/settingslib_ripple_color"> <item android:id="@android:id/background"> - <shape> - <corners android:radius="10dp"/> - <solid android:color="@android:color/system_accent2_100"/> - </shape> + <layer-list + android:paddingMode="stack" + android:paddingStart="0dp" + android:paddingEnd="12dp"> + + <item> + <shape> + <corners android:radius="10dp"/> + <solid android:color="@android:color/system_accent2_100"/> + </shape> + </item> + </layer-list> </item> </ripple> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml index 29fdab13c717..11546c8ed3d9 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml @@ -25,6 +25,4 @@ <dimen name="settingslib_listPreferredItemPaddingStart">24dp</dimen> <!-- Right padding of the preference --> <dimen name="settingslib_listPreferredItemPaddingEnd">24dp</dimen> - - <dimen name="settingslib_spinner_height">36dp</dimen> </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml index b12c6d2850c4..3597ee9b08ee 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml @@ -45,24 +45,4 @@ <item name="android:progressDrawable">@drawable/settingslib_progress_horizontal</item> <item name="android:scaleY">0.5</item> </style> - - <style name="Spinner.SettingsLib" - parent="android:style/Widget.Material.Spinner"> - <item name="android:background">@drawable/settingslib_spinner_background</item> - <item name="android:popupBackground">@drawable/settingslib_spinner_dropdown_background</item> - <item name="android:dropDownVerticalOffset">48dp</item> - <item name="android:layout_marginTop">16dp</item> - <item name="android:layout_marginBottom">8dp</item> - </style> - - <style name="SpinnerItem.SettingsLib" - parent="@android:style/Widget.DeviceDefault.TextView.SpinnerItem"> - <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item> - </style> - - <style name="SpinnerDropDownItem.SettingsLib" - parent="@android:style/Widget.Material.DropDownItem.Spinner"> - <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item> - </style> - </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml index 4f426a3bf10c..69c122c9992e 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml @@ -17,7 +17,7 @@ <resources> <!-- Only using in Settings application --> - <style name="Theme.SettingsBase" parent="@android:style/Theme.DeviceDefault.Settings" > + <style name="Theme.SettingsBase_v31" parent="@android:style/Theme.DeviceDefault.Settings" > <item name="android:textAppearanceListItem">@style/TextAppearance.PreferenceTitle.SettingsLib</item> <item name="android:listPreferredItemPaddingStart">@dimen/settingslib_listPreferredItemPaddingStart</item> <item name="android:listPreferredItemPaddingLeft">@dimen/settingslib_listPreferredItemPaddingStart</item> @@ -26,11 +26,10 @@ <item name="preferenceTheme">@style/PreferenceTheme.SettingsLib</item> <item name="android:switchStyle">@style/Switch.SettingsLib</item> <item name="android:progressBarStyleHorizontal">@style/HorizontalProgressBar.SettingsLib</item> - <item name="android:spinnerStyle">@style/Spinner.SettingsLib</item> - <item name="android:spinnerItemStyle">@style/SpinnerItem.SettingsLib</item> - <item name="android:spinnerDropDownItemStyle">@style/SpinnerDropDownItem.SettingsLib</item> </style> + <style name="Theme.SettingsBase" parent="Theme.SettingsBase_v31" /> + <!-- Using in SubSettings page including injected settings page --> <style name="Theme.SubSettingsBase" parent="Theme.SettingsBase"> <!-- Suppress the built-in action bar --> diff --git a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_arrow_drop_down.xml b/packages/SettingsLib/SettingsTheme/res/values-v33/dimens.xml index 6ed215dd2dbb..bec807b395f7 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_arrow_drop_down.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v33/dimens.xml @@ -15,12 +15,6 @@ limitations under the License. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:viewportWidth="24" - android:viewportHeight="24" - android:width="24dp" - android:height="24dp"> - <path - android:pathData="M7 10l5 5 5 -5z" - android:fillColor="?android:attr/textColorPrimary"/> -</vector> +<resources> + <dimen name="settingslib_spinner_height">36dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/values-v33/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v33/styles.xml new file mode 100644 index 000000000000..15fdfe49b0cb --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-v33/styles.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. + --> +<resources> + <style name="Spinner.SettingsLib" + parent="android:style/Widget.Material.Spinner"> + <item name="android:background">@drawable/settingslib_spinner_background</item> + <item name="android:popupBackground">@drawable/settingslib_spinner_dropdown_background + </item> + <item name="android:dropDownVerticalOffset">48dp</item> + <item name="android:layout_marginTop">16dp</item> + <item name="android:layout_marginBottom">8dp</item> + </style> + + <style name="SpinnerItem.SettingsLib" + parent="@android:style/Widget.DeviceDefault.TextView.SpinnerItem"> + <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item> + <item name="android:paddingStart">16dp</item> + </style> + + <style name="SpinnerDropDownItem.SettingsLib" + parent="@android:style/Widget.Material.DropDownItem.Spinner"> + <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item> + <item name="android:paddingStart">16dp</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/values-v33/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v33/themes.xml new file mode 100644 index 000000000000..24e3c46b39ce --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-v33/themes.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. + --> + +<resources> + <style name="Theme.SettingsBase" parent="Theme.SettingsBase_v31" > + <item name="android:spinnerStyle">@style/Spinner.SettingsLib</item> + <item name="android:spinnerItemStyle">@style/SpinnerItem.SettingsLib</item> + <item name="android:spinnerDropDownItemStyle">@style/SpinnerDropDownItem.SettingsLib</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles.xml b/packages/SettingsLib/SettingsTheme/res/values/styles.xml index fa27bb692fe8..aaab0f041fe3 100644 --- a/packages/SettingsLib/SettingsTheme/res/values/styles.xml +++ b/packages/SettingsLib/SettingsTheme/res/values/styles.xml @@ -26,10 +26,4 @@ <style name="TextAppearance.CategoryTitle.SettingsLib" parent="@android:style/TextAppearance.DeviceDefault.Medium"> </style> - - <style name="Spinner.SettingsLib" - parent="android:style/Widget.Material.Spinner"> - <item name="android:background">@drawable/settingslib_spinner_background</item> - <item name="android:dropDownVerticalOffset">48dp</item> - </style> </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values/themes.xml b/packages/SettingsLib/SettingsTheme/res/values/themes.xml index 8dc0f3c1ff2b..2d881d1a8a7b 100644 --- a/packages/SettingsLib/SettingsTheme/res/values/themes.xml +++ b/packages/SettingsLib/SettingsTheme/res/values/themes.xml @@ -19,7 +19,6 @@ <!-- Only using in Settings application --> <style name="Theme.SettingsBase" parent="@android:style/Theme.DeviceDefault.Settings"> <item name="preferenceTheme">@style/PreferenceThemeOverlay</item> - <item name="android:spinnerStyle">@style/Spinner.SettingsLib</item> </style> <!-- Using in SubSettings page including injected settings page --> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index eb7fea3eb89a..6d7172ec0fbe 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -224,6 +224,7 @@ <uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS" /> <uses-permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> + <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" /> <!-- It's like, reality, but, you know, virtual --> <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" /> diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index 7e8112a8885b..4f7d09963fc6 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -135,6 +135,15 @@ asked for it --> android:layout_height="wrap_content" style="@*android:style/TextAppearance.DeviceDefault.Notification" /> + <!-- Non configurable app/channel text. appears instead of @+id/interruptiveness_settings--> + <TextView + android:id="@+id/non_configurable_call_text" + android:text="@string/notification_unblockable_call_desc" + android:visibility="gone" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@*android:style/TextAppearance.DeviceDefault.Notification" /> + <!-- Non configurable multichannel text. appears instead of @+id/interruptiveness_settings--> <TextView android:id="@+id/non_configurable_multichannel_text" diff --git a/packages/SystemUI/res/layout/rounded_corners_bottom.xml b/packages/SystemUI/res/layout/rounded_corners_bottom.xml deleted file mode 100644 index bb6d4bddf25a..000000000000 --- a/packages/SystemUI/res/layout/rounded_corners_bottom.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** Copyright 2020, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. ---> -<com.android.systemui.RegionInterceptingFrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/rounded_corners_bottom" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <ImageView - android:id="@+id/left" - android:layout_width="12dp" - android:layout_height="12dp" - android:layout_gravity="left|bottom" - android:tint="#ff000000" - android:visibility="gone" - android:src="@drawable/rounded_corner_bottom"/> - - <ImageView - android:id="@+id/right" - android:layout_width="12dp" - android:layout_height="12dp" - android:tint="#ff000000" - android:visibility="gone" - android:layout_gravity="right|bottom" - android:src="@drawable/rounded_corner_bottom"/> - -</com.android.systemui.RegionInterceptingFrameLayout> diff --git a/packages/SystemUI/res/layout/rounded_corners_top.xml b/packages/SystemUI/res/layout/rounded_corners_top.xml deleted file mode 100644 index 46648c88d921..000000000000 --- a/packages/SystemUI/res/layout/rounded_corners_top.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** Copyright 2020, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. ---> -<com.android.systemui.RegionInterceptingFrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/rounded_corners_top" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <ImageView - android:id="@+id/left" - android:layout_width="12dp" - android:layout_height="12dp" - android:layout_gravity="left|top" - android:tint="#ff000000" - android:visibility="gone" - android:src="@drawable/rounded_corner_top"/> - - <ImageView - android:id="@+id/right" - android:layout_width="12dp" - android:layout_height="12dp" - android:tint="#ff000000" - android:visibility="gone" - android:layout_gravity="right|top" - android:src="@drawable/rounded_corner_top"/> - -</com.android.systemui.RegionInterceptingFrameLayout> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index ff71b4f5e405..5eacc3e6006b 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -170,5 +170,11 @@ <item type="id" name="action_move_bottom_right"/> <item type="id" name="action_move_to_edge_and_hide"/> <item type="id" name="action_move_out_edge_and_show"/> + + <!-- rounded corner view id --> + <item type="id" name="rounded_corner_top_left"/> + <item type="id" name="rounded_corner_top_right"/> + <item type="id" name="rounded_corner_bottom_left"/> + <item type="id" name="rounded_corner_bottom_right"/> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index b248efe93e98..0f5115b5c0f5 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1373,6 +1373,9 @@ <!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. --> <string name="notification_unblockable_desc">These notifications can\'t be modified.</string> + <!-- Notification: Control panel: Label that displays when a notification cannot be blocked because it's attached to a phone/voip call. --> + <string name="notification_unblockable_call_desc">Call notifications can\'t be modified.</string> + <!-- Notification: Control panel: label that displays when viewing settings for a group of notifications posted to multiple channels. --> <string name="notification_multichannel_desc">This group of notifications cannot be configured here</string> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java index 32299f5643db..5bd81a42a814 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java @@ -86,6 +86,7 @@ public class WindowManagerWrapper { public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = InsetsState.ITYPE_RIGHT_TAPPABLE_ELEMENT; public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; + public static final int ITYPE_SIZE = InsetsState.SIZE; public static final int ANIMATION_DURATION_RESIZE = InsetsController.ANIMATION_DURATION_RESIZE; public static final Interpolator RESIZE_INTERPOLATOR = InsetsController.RESIZE_INTERPOLATOR; diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 43d91a24bd3f..9b091018de9f 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -77,6 +77,7 @@ import com.android.systemui.decor.DecorProviderFactory; import com.android.systemui.decor.DecorProviderKt; import com.android.systemui.decor.OverlayWindow; import com.android.systemui.decor.PrivacyDotDecorProviderFactory; +import com.android.systemui.decor.RoundedCornerDecorProviderFactory; import com.android.systemui.decor.RoundedCornerResDelegate; import com.android.systemui.qs.SettingObserver; import com.android.systemui.settings.UserTracker; @@ -137,6 +138,9 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab @VisibleForTesting protected RoundedCornerResDelegate mRoundedCornerResDelegate; @VisibleForTesting + protected DecorProviderFactory mRoundedCornerFactory; + private int mProviderRefreshToken = 0; + @VisibleForTesting protected OverlayWindow[] mOverlays = null; @VisibleForTesting @Nullable @@ -282,16 +286,58 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return mDotFactory.getHasProviders(); } + @NonNull + private List<DecorProvider> getProviders(boolean hasHwLayer) { + List<DecorProvider> decorProviders = new ArrayList<>(mDotFactory.getProviders()); + if (!hasHwLayer) { + decorProviders.addAll(mRoundedCornerFactory.getProviders()); + } + return decorProviders; + } + + private void updateDisplayIdToProviderFactories() { + mDotFactory.onDisplayUniqueIdChanged(mDisplayUniqueId); + mRoundedCornerFactory.onDisplayUniqueIdChanged(mDisplayUniqueId); + } + + /** + * Check that newProviders is the same list with decorProviders inside mOverlay. + * @param newProviders expected comparing DecorProviders + * @return true if same provider list + */ + @VisibleForTesting + boolean hasSameProviders(@NonNull List<DecorProvider> newProviders) { + final ArrayList<Integer> overlayViewIds = new ArrayList<>(); + if (mOverlays != null) { + for (OverlayWindow overlay : mOverlays) { + if (overlay == null) { + continue; + } + overlayViewIds.addAll(overlay.getViewIds()); + } + } + if (overlayViewIds.size() != newProviders.size()) { + return false; + } + + for (DecorProvider provider: newProviders) { + if (!overlayViewIds.contains(provider.getViewId())) { + return false; + } + } + return true; + } + private void startOnScreenDecorationsThread() { mRotation = mContext.getDisplay().getRotation(); mDisplayUniqueId = mContext.getDisplay().getUniqueId(); mRoundedCornerResDelegate = new RoundedCornerResDelegate(mContext.getResources(), mDisplayUniqueId); + mRoundedCornerFactory = new RoundedCornerDecorProviderFactory(mRoundedCornerResDelegate); mWindowManager = mContext.getSystemService(WindowManager.class); mDisplayManager = mContext.getSystemService(DisplayManager.class); mHwcScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport(); - updateRoundedCornerDrawable(); - updateRoundedCornerRadii(); + updateHwLayerRoundedCornerDrawable(); setupDecorations(); setupCameraListener(); @@ -343,18 +389,27 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab final String newUniqueId = mContext.getDisplay().getUniqueId(); if (!Objects.equals(newUniqueId, mDisplayUniqueId)) { mDisplayUniqueId = newUniqueId; - mRoundedCornerResDelegate.reloadAll(newUniqueId); final DisplayDecorationSupport newScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport(); - // When the value of mSupportHwcScreenDecoration is changed, re-setup the whole - // screen decoration. - if (!eq(newScreenDecorationSupport, mHwcScreenDecorationSupport)) { + + updateDisplayIdToProviderFactories(); + + // When providers or the value of mSupportHwcScreenDecoration is changed, + // re-setup the whole screen decoration. + if (!hasSameProviders(getProviders(newScreenDecorationSupport != null)) + || !eq(newScreenDecorationSupport, mHwcScreenDecorationSupport)) { mHwcScreenDecorationSupport = newScreenDecorationSupport; removeAllOverlays(); setupDecorations(); return; } - updateRoundedCornerDrawable(); + + if (mScreenDecorHwcLayer != null) { + updateHwLayerRoundedCornerDrawable(); + updateHwLayerRoundedCornerSize(); + } + + updateOverlayProviderViews(); } if (mCutoutViews != null) { final int size = mCutoutViews.length; @@ -369,7 +424,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab if (mScreenDecorHwcLayer != null) { mScreenDecorHwcLayer.onDisplayChanged(displayId); } - updateOrientation(); } }; @@ -396,6 +450,19 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return null; } + private void removeRedundantOverlayViews(@NonNull List<DecorProvider> decorProviders) { + if (mOverlays == null) { + return; + } + int[] viewIds = decorProviders.stream().mapToInt(DecorProvider::getViewId).toArray(); + for (final OverlayWindow overlay : mOverlays) { + if (overlay == null) { + continue; + } + overlay.removeRedundantViews(viewIds); + } + } + private void removeOverlayView(@IdRes int id) { if (mOverlays == null) { return; @@ -411,9 +478,10 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } private void setupDecorations() { - List<DecorProvider> decorProviders = mDotFactory.getProviders(); + if (hasRoundedCorners() || shouldDrawCutout() || isPrivacyDotEnabled()) { + List<DecorProvider> decorProviders = getProviders(mHwcScreenDecorationSupport != null); + removeRedundantOverlayViews(decorProviders); - if (hasRoundedCorners() || shouldDrawCutout() || !decorProviders.isEmpty()) { if (mHwcScreenDecorationSupport != null) { createHwcOverlay(); } else { @@ -427,7 +495,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab Pair<List<DecorProvider>, List<DecorProvider>> pair = DecorProviderKt.partitionAlignedBound(decorProviders, i); decorProviders = pair.getSecond(); - createOverlay(i, cutout, pair.getFirst(), isOnlyPrivacyDotInSwLayer); + createOverlay(i, pair.getFirst(), isOnlyPrivacyDotInSwLayer); } else { removeOverlay(i); } @@ -560,7 +628,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab private void createOverlay( @BoundsPosition int pos, - @Nullable DisplayCutout cutout, @NonNull List<DecorProvider> decorProviders, boolean isOnlyPrivacyDotInSwLayer) { if (mOverlays == null) { @@ -568,14 +635,12 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } if (mOverlays[pos] != null) { - // When mOverlay[pos] is not null and only privacy dot in sw layer, use privacy dot - // view's visibility - mOverlays[pos].getRootView().setVisibility( - getWindowVisibility(mOverlays[pos], isOnlyPrivacyDotInSwLayer)); + initOverlay(mOverlays[pos], decorProviders, isOnlyPrivacyDotInSwLayer); return; } - mOverlays[pos] = overlayForPosition(pos, decorProviders, isOnlyPrivacyDotInSwLayer); + mOverlays[pos] = new OverlayWindow(mContext); + initOverlay(mOverlays[pos], decorProviders, isOnlyPrivacyDotInSwLayer); final ViewGroup overlayView = mOverlays[pos].getRootView(); overlayView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); overlayView.setAlpha(0); @@ -590,7 +655,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mCutoutViews[pos] = new DisplayCutoutView(mContext, pos); mCutoutViews[pos].setColor(mTintColor); overlayView.addView(mCutoutViews[pos]); - updateView(pos, cutout); + mCutoutViews[pos].updateRotation(mRotation); } mWindowManager.addView(overlayView, getWindowLayoutParams(pos)); @@ -641,42 +706,24 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } /** - * Allow overrides for top/bottom positions + * Init OverlayWindow with decorProviders */ - private OverlayWindow overlayForPosition( - @BoundsPosition int pos, + private void initOverlay( + @NonNull OverlayWindow overlay, @NonNull List<DecorProvider> decorProviders, boolean isOnlyPrivacyDotInSwLayer) { - final OverlayWindow currentOverlay = new OverlayWindow(LayoutInflater.from(mContext), pos); - decorProviders.forEach(provider -> { - removeOverlayView(provider.getViewId()); - currentOverlay.addDecorProvider(provider, mRotation); - }); - // When only privacy dot in mOverlay, set the initial visibility of mOverlays to - // INVISIBLE and set it to VISIBLE when the privacy dot is showing. - if (isOnlyPrivacyDotInSwLayer) { - currentOverlay.getRootView().setVisibility(View.INVISIBLE); - } - return currentOverlay; - } - - private void updateView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { - if (mOverlays == null || mOverlays[pos] == null || mHwcScreenDecorationSupport != null) { - return; - } - - // update rounded corner view rotation - updateRoundedCornerView(pos, R.id.left, cutout); - updateRoundedCornerView(pos, R.id.right, cutout); - updateRoundedCornerSize( - mRoundedCornerResDelegate.getTopRoundedSize(), - mRoundedCornerResDelegate.getBottomRoundedSize()); - updateRoundedCornerImageView(); - - // update cutout view rotation - if (mCutoutViews != null && mCutoutViews[pos] != null) { - mCutoutViews[pos].updateRotation(mRotation); + if (!overlay.hasSameProviders(decorProviders)) { + decorProviders.forEach(provider -> { + if (overlay.getView(provider.getViewId()) != null) { + return; + } + removeOverlayView(provider.getViewId()); + overlay.addDecorProvider(provider, mRotation); + }); } + // Use visibility of privacy dot views if only privacy dot in sw layer + overlay.getRootView().setVisibility( + getWindowVisibility(overlay, isOnlyPrivacyDotInSwLayer)); } @VisibleForTesting @@ -849,7 +896,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab int oldRotation = mRotation; mPendingRotationChange = false; updateOrientation(); - updateRoundedCornerRadii(); if (DEBUG) Log.i(TAG, "onConfigChanged from rot " + oldRotation + " to " + mRotation); setupDecorations(); if (mOverlays != null) { @@ -910,109 +956,32 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mDotViewController.setNewRotation(newRotation); } - if (mPendingRotationChange) { - return; - } - if (newRotation != mRotation) { + if (!mPendingRotationChange && newRotation != mRotation) { mRotation = newRotation; if (mScreenDecorHwcLayer != null) { mScreenDecorHwcLayer.pendingRotationChange = false; mScreenDecorHwcLayer.updateRotation(mRotation); + updateHwLayerRoundedCornerSize(); + updateHwLayerRoundedCornerDrawable(); } - if (mOverlays != null) { - updateLayoutParams(); - final DisplayCutout cutout = getCutout(); - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] == null) { + updateLayoutParams(); + // update cutout view rotation + if (mCutoutViews != null) { + for (final DisplayCutoutView cutoutView: mCutoutViews) { + if (cutoutView == null) { continue; } - updateView(i, cutout); + cutoutView.updateRotation(mRotation); } } } - } - private void updateRoundedCornerRadii() { - // We should eventually move to just using the intrinsic size of the drawables since - // they should be sized to the exact pixels they want to cover. Therefore I'm purposely not - // upgrading all of the configs to contain (width, height) pairs. Instead assume that a - // device configured using the single integer config value is okay with drawing the corners - // as a square - final Size oldRoundedDefaultTop = mRoundedCornerResDelegate.getTopRoundedSize(); - final Size oldRoundedDefaultBottom = mRoundedCornerResDelegate.getBottomRoundedSize(); - mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId); - final Size newRoundedDefaultTop = mRoundedCornerResDelegate.getTopRoundedSize(); - final Size newRoundedDefaultBottom = mRoundedCornerResDelegate.getBottomRoundedSize(); - - if (oldRoundedDefaultTop.getWidth() != newRoundedDefaultTop.getWidth() - || oldRoundedDefaultBottom.getWidth() != newRoundedDefaultBottom.getWidth()) { - onTuningChanged(SIZE, null); - } - } - - private void updateRoundedCornerView(@BoundsPosition int pos, int id, - @Nullable DisplayCutout cutout) { - final View rounded = mOverlays[pos].getRootView().findViewById(id); - if (rounded == null) { - return; - } - rounded.setVisibility(View.GONE); - if (shouldShowSwLayerRoundedCorner(pos, cutout)) { - final int gravity = getRoundedCornerGravity(pos, id == R.id.left); - ((FrameLayout.LayoutParams) rounded.getLayoutParams()).gravity = gravity; - setRoundedCornerOrientation(rounded, gravity); - rounded.setVisibility(View.VISIBLE); - } - } - - private int getRoundedCornerGravity(@BoundsPosition int pos, boolean isStart) { - final int rotatedPos = getBoundPositionFromRotation(pos, mRotation); - switch (rotatedPos) { - case BOUNDS_POSITION_LEFT: - return isStart ? Gravity.TOP | Gravity.LEFT : Gravity.BOTTOM | Gravity.LEFT; - case BOUNDS_POSITION_TOP: - return isStart ? Gravity.TOP | Gravity.LEFT : Gravity.TOP | Gravity.RIGHT; - case BOUNDS_POSITION_RIGHT: - return isStart ? Gravity.TOP | Gravity.RIGHT : Gravity.BOTTOM | Gravity.RIGHT; - case BOUNDS_POSITION_BOTTOM: - return isStart ? Gravity.BOTTOM | Gravity.LEFT : Gravity.BOTTOM | Gravity.RIGHT; - default: - throw new IllegalArgumentException("Incorrect position: " + rotatedPos); - } + // update all provider views inside overlay + updateOverlayProviderViews(); } - /** - * Configures the rounded corner drawable's view matrix based on the gravity. - * - * The gravity describes which corner to configure for, and the drawable we are rotating is - * assumed to be oriented for the top-left corner of the device regardless of the target corner. - * Therefore we need to rotate 180 degrees to get a bottom-left corner, and mirror in the x- or - * y-axis for the top-right and bottom-left corners. - */ - private void setRoundedCornerOrientation(View corner, int gravity) { - corner.setRotation(0); - corner.setScaleX(1); - corner.setScaleY(1); - switch (gravity) { - case Gravity.TOP | Gravity.LEFT: - return; - case Gravity.TOP | Gravity.RIGHT: - corner.setScaleX(-1); // flip X axis - return; - case Gravity.BOTTOM | Gravity.LEFT: - corner.setScaleY(-1); // flip Y axis - return; - case Gravity.BOTTOM | Gravity.RIGHT: - corner.setRotation(180); - return; - default: - throw new IllegalArgumentException("Unsupported gravity: " + gravity); - } - } private boolean hasRoundedCorners() { - return mRoundedCornerResDelegate.getBottomRoundedSize().getWidth() > 0 - || mRoundedCornerResDelegate.getTopRoundedSize().getWidth() > 0 - || mRoundedCornerResDelegate.isMultipleRadius(); + return mRoundedCornerFactory.getHasProviders(); } private boolean isDefaultShownOverlayPos(@BoundsPosition int pos, @@ -1066,6 +1035,19 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab context.getResources(), context.getDisplay().getUniqueId()); } + private void updateOverlayProviderViews() { + if (mOverlays == null) { + return; + } + ++mProviderRefreshToken; + for (final OverlayWindow overlay: mOverlays) { + if (overlay == null) { + continue; + } + overlay.onReloadResAndMeasure(null, mProviderRefreshToken, mRotation, mDisplayUniqueId); + } + } + private void updateLayoutParams() { if (mOverlays == null) { return; @@ -1085,63 +1067,33 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return; } mExecutor.execute(() -> { - if (mOverlays == null) return; - if (SIZE.equals(key)) { - boolean hasReloadRoundedCornerRes = false; - if (newValue != null) { - try { - mRoundedCornerResDelegate.updateTuningSizeFactor( - Integer.parseInt(newValue)); - hasReloadRoundedCornerRes = true; - } catch (Exception e) { - } - } - - // When onTuningChanged() is not called through updateRoundedCornerRadii(), - // we need to reload rounded corner res to prevent incorrect dimen - if (!hasReloadRoundedCornerRes) { - mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId); + if (mOverlays == null || !SIZE.equals(key)) { + return; + } + ++mProviderRefreshToken; + try { + final int sizeFactor = Integer.parseInt(newValue); + mRoundedCornerResDelegate.updateTuningSizeFactor(sizeFactor, mProviderRefreshToken); + } catch (NumberFormatException e) { + mRoundedCornerResDelegate.updateTuningSizeFactor(null, mProviderRefreshToken); + } + Integer[] filterIds = { + R.id.rounded_corner_top_left, + R.id.rounded_corner_top_right, + R.id.rounded_corner_bottom_left, + R.id.rounded_corner_bottom_right + }; + for (final OverlayWindow overlay: mOverlays) { + if (overlay == null) { + continue; } - - updateRoundedCornerSize( - mRoundedCornerResDelegate.getTopRoundedSize(), - mRoundedCornerResDelegate.getBottomRoundedSize()); + overlay.onReloadResAndMeasure(filterIds, mProviderRefreshToken, mRotation, + mDisplayUniqueId); } + updateHwLayerRoundedCornerSize(); }); } - private void updateRoundedCornerDrawable() { - mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId); - updateRoundedCornerImageView(); - } - - private void updateRoundedCornerImageView() { - final Drawable top = mRoundedCornerResDelegate.getTopRoundedDrawable(); - final Drawable bottom = mRoundedCornerResDelegate.getBottomRoundedDrawable(); - - if (mScreenDecorHwcLayer != null) { - mScreenDecorHwcLayer.updateRoundedCornerDrawable(top, bottom); - return; - } - - if (mOverlays == null) { - return; - } - final ColorStateList colorStateList = ColorStateList.valueOf(mTintColor); - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] == null) { - continue; - } - final ViewGroup overlayView = mOverlays[i].getRootView(); - ((ImageView) overlayView.findViewById(R.id.left)).setImageTintList(colorStateList); - ((ImageView) overlayView.findViewById(R.id.right)).setImageTintList(colorStateList); - ((ImageView) overlayView.findViewById(R.id.left)).setImageDrawable( - isTopRoundedCorner(i, R.id.left) ? top : bottom); - ((ImageView) overlayView.findViewById(R.id.right)).setImageDrawable( - isTopRoundedCorner(i, R.id.right) ? top : bottom); - } - } - private void updateHwLayerRoundedCornerDrawable() { if (mScreenDecorHwcLayer == null) { return; @@ -1156,25 +1108,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mScreenDecorHwcLayer.updateRoundedCornerDrawable(topDrawable, bottomDrawable); } - @VisibleForTesting - boolean isTopRoundedCorner(@BoundsPosition int pos, int id) { - switch (pos) { - case BOUNDS_POSITION_LEFT: - case BOUNDS_POSITION_RIGHT: - if (mRotation == ROTATION_270) { - return id == R.id.left ? false : true; - } else { - return id == R.id.left ? true : false; - } - case BOUNDS_POSITION_TOP: - return true; - case BOUNDS_POSITION_BOTTOM: - return false; - default: - throw new IllegalArgumentException("Unknown bounds position"); - } - } - private void updateHwLayerRoundedCornerSize() { if (mScreenDecorHwcLayer == null) { return; @@ -1186,28 +1119,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mScreenDecorHwcLayer.updateRoundedCornerSize(topWidth, bottomWidth); } - private void updateRoundedCornerSize(Size sizeTop, Size sizeBottom) { - - if (mScreenDecorHwcLayer != null) { - mScreenDecorHwcLayer.updateRoundedCornerSize(sizeTop.getWidth(), sizeBottom.getWidth()); - return; - } - - if (mOverlays == null) { - return; - } - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] == null) { - continue; - } - final ViewGroup overlayView = mOverlays[i].getRootView(); - setSize(overlayView.findViewById(R.id.left), - isTopRoundedCorner(i, R.id.left) ? sizeTop : sizeBottom); - setSize(overlayView.findViewById(R.id.right), - isTopRoundedCorner(i, R.id.right) ? sizeTop : sizeBottom); - } - } - @VisibleForTesting protected void setSize(View view, Size pixelSize) { LayoutParams params = view.getLayoutParams(); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index b2673e923008..233f3648aeb3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -56,7 +56,9 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.util.concurrency.DelayableExecutor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -110,6 +112,8 @@ public class AuthContainerView extends LinearLayout @ContainerState private int mContainerState = STATE_UNKNOWN; private final Set<Integer> mFailedModalities = new HashSet<Integer>(); + private final @Background DelayableExecutor mBackgroundExecutor; + // Non-null only if the dialog is in the act of dismissing and has not sent the reason yet. @Nullable @AuthDialogCallback.DismissedReason private Integer mPendingCallbackReason; // HAT received from LockSettingsService when credential is verified. @@ -192,7 +196,7 @@ public class AuthContainerView extends LinearLayout return this; } - public AuthContainerView build(int[] sensorIds, + public AuthContainerView build(@Background DelayableExecutor bgExecutor, int[] sensorIds, @Nullable List<FingerprintSensorPropertiesInternal> fpProps, @Nullable List<FaceSensorPropertiesInternal> faceProps, @NonNull WakefulnessLifecycle wakefulnessLifecycle, @@ -200,7 +204,7 @@ public class AuthContainerView extends LinearLayout @NonNull LockPatternUtils lockPatternUtils) { mConfig.mSensorIds = sensorIds; return new AuthContainerView(mConfig, fpProps, faceProps, wakefulnessLifecycle, - userManager, lockPatternUtils, new Handler(Looper.getMainLooper())); + userManager, lockPatternUtils, new Handler(Looper.getMainLooper()), bgExecutor); } } @@ -253,7 +257,8 @@ public class AuthContainerView extends LinearLayout @NonNull WakefulnessLifecycle wakefulnessLifecycle, @NonNull UserManager userManager, @NonNull LockPatternUtils lockPatternUtils, - @NonNull Handler mainHandler) { + @NonNull Handler mainHandler, + @NonNull @Background DelayableExecutor bgExecutor) { super(config.mContext); mConfig = config; @@ -277,6 +282,7 @@ public class AuthContainerView extends LinearLayout mBackgroundView = mFrameLayout.findViewById(R.id.background); mPanelView = mFrameLayout.findViewById(R.id.panel); mPanelController = new AuthPanelController(mContext, mPanelView); + mBackgroundExecutor = bgExecutor; // Inflate biometric view only if necessary. if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) { @@ -384,6 +390,7 @@ public class AuthContainerView extends LinearLayout mCredentialView.setPromptInfo(mConfig.mPromptInfo); mCredentialView.setPanelController(mPanelController, animatePanel); mCredentialView.setShouldAnimateContents(animateContents); + mCredentialView.setBackgroundExecutor(mBackgroundExecutor); mFrameLayout.addView(mCredentialView); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index aaf18b309db2..b05bc245a79f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -64,11 +64,13 @@ import com.android.internal.widget.LockPatternUtils; import com.android.systemui.CoreStartable; import com.android.systemui.assist.ui.DisplayUtils; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.Execution; import java.util.ArrayList; @@ -139,6 +141,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba private boolean mAllAuthenticatorsRegistered; @NonNull private final UserManager mUserManager; @NonNull private final LockPatternUtils mLockPatternUtils; + private final @Background DelayableExecutor mBackgroundExecutor; @VisibleForTesting final TaskStackListener mTaskStackListener = new TaskStackListener() { @@ -507,13 +510,15 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba @NonNull UserManager userManager, @NonNull LockPatternUtils lockPatternUtils, @NonNull StatusBarStateController statusBarStateController, - @Main Handler handler) { + @Main Handler handler, + @Background DelayableExecutor bgExecutor) { super(context); mExecution = execution; mWakefulnessLifecycle = wakefulnessLifecycle; mUserManager = userManager; mLockPatternUtils = lockPatternUtils; mHandler = handler; + mBackgroundExecutor = bgExecutor; mCommandQueue = commandQueue; mActivityTaskManager = activityTaskManager; mFingerprintManager = fingerprintManager; @@ -839,6 +844,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba // Create a new dialog but do not replace the current one yet. final AuthDialog newDialog = buildDialog( + mBackgroundExecutor, promptInfo, requireConfirmation, userId, @@ -934,9 +940,9 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } } - protected AuthDialog buildDialog(PromptInfo promptInfo, boolean requireConfirmation, - int userId, int[] sensorIds, String opPackageName, - boolean skipIntro, long operationId, long requestId, + protected AuthDialog buildDialog(@Background DelayableExecutor bgExecutor, + PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds, + String opPackageName, boolean skipIntro, long operationId, long requestId, @BiometricMultiSensorMode int multiSensorConfig, @NonNull WakefulnessLifecycle wakefulnessLifecycle, @NonNull UserManager userManager, @@ -951,8 +957,8 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba .setOperationId(operationId) .setRequestId(requestId) .setMultiSensorConfig(multiSensorConfig) - .build(sensorIds, mFpProps, mFaceProps, wakefulnessLifecycle, userManager, - lockPatternUtils); + .build(bgExecutor, sensorIds, mFpProps, mFaceProps, wakefulnessLifecycle, + userManager, lockPatternUtils); } /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java index ed84a37682e4..4fa835e038ec 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java @@ -53,6 +53,8 @@ import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.VerifyCredentialResponse; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.util.concurrency.DelayableExecutor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -99,6 +101,8 @@ public abstract class AuthCredentialView extends LinearLayout { protected int mEffectiveUserId; protected ErrorTimer mErrorTimer; + protected @Background DelayableExecutor mBackgroundExecutor; + interface Callback { void onCredentialMatched(byte[] attestation); } @@ -217,6 +221,10 @@ public abstract class AuthCredentialView extends LinearLayout { mContainerView = containerView; } + void setBackgroundExecutor(@Background DelayableExecutor bgExecutor) { + mBackgroundExecutor = bgExecutor; + } + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); @@ -377,27 +385,33 @@ public abstract class AuthCredentialView extends LinearLayout { } private void showLastAttemptBeforeWipeDialog() { - final AlertDialog alertDialog = new AlertDialog.Builder(mContext) - .setTitle(R.string.biometric_dialog_last_attempt_before_wipe_dialog_title) - .setMessage( - getLastAttemptBeforeWipeMessage(getUserTypeForWipe(), mCredentialType)) - .setPositiveButton(android.R.string.ok, null) - .create(); - alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); - alertDialog.show(); + mBackgroundExecutor.execute(() -> { + final AlertDialog alertDialog = new AlertDialog.Builder(mContext) + .setTitle(R.string.biometric_dialog_last_attempt_before_wipe_dialog_title) + .setMessage( + getLastAttemptBeforeWipeMessage(getUserTypeForWipe(), mCredentialType)) + .setPositiveButton(android.R.string.ok, null) + .create(); + alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); + mHandler.post(alertDialog::show); + }); } private void showNowWipingDialog() { - final AlertDialog alertDialog = new AlertDialog.Builder(mContext) - .setMessage(getNowWipingMessage(getUserTypeForWipe())) - .setPositiveButton( - com.android.settingslib.R.string.failed_attempts_now_wiping_dialog_dismiss, - null /* OnClickListener */) - .setOnDismissListener( - dialog -> mContainerView.animateAway(AuthDialogCallback.DISMISSED_ERROR)) - .create(); - alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); - alertDialog.show(); + mBackgroundExecutor.execute(() -> { + String nowWipingMessage = getNowWipingMessage(getUserTypeForWipe()); + final AlertDialog alertDialog = new AlertDialog.Builder(mContext) + .setMessage(nowWipingMessage) + .setPositiveButton( + com.android.settingslib.R.string.failed_attempts_now_wiping_dialog_dismiss, + null /* OnClickListener */) + .setOnDismissListener( + dialog -> mContainerView.animateAway( + AuthDialogCallback.DISMISSED_ERROR)) + .create(); + alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); + mHandler.post(alertDialog::show); + }); } private @UserType int getUserTypeForWipe() { @@ -412,6 +426,7 @@ public abstract class AuthCredentialView extends LinearLayout { } } + // This should not be called on the main thread to avoid making an IPC. private String getLastAttemptBeforeWipeMessage( @UserType int userType, @Utils.CredentialType int credentialType) { switch (userType) { @@ -442,6 +457,7 @@ public abstract class AuthCredentialView extends LinearLayout { } } + // This should not be called on the main thread to avoid making an IPC. private String getLastAttemptBeforeWipeProfileMessage( @Utils.CredentialType int credentialType) { return mDevicePolicyManager.getResources().getString( diff --git a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt index 3543bb4ab9e9..03ee8b11ab41 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt @@ -15,8 +15,8 @@ */ package com.android.systemui.decor +import android.content.Context import android.view.DisplayCutout -import android.view.LayoutInflater import android.view.Surface import android.view.View import android.view.ViewGroup @@ -38,9 +38,20 @@ abstract class DecorProvider { /** The aligned bounds for the view which is created through inflateView() */ abstract val alignedBounds: List<Int> + /** + * Called when res info changed. + * Child provider needs to implement it if its view needs to be updated. + */ + abstract fun onReloadResAndMeasure( + view: View, + reloadToken: Int, + @Surface.Rotation rotation: Int, + displayUniqueId: String? = null + ) + /** Inflate view into parent as current rotation */ abstract fun inflateView( - inflater: LayoutInflater, + context: Context, parent: ViewGroup, @Surface.Rotation rotation: Int ): View diff --git a/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt index c60cad8419d2..cc4096fb3b19 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt @@ -19,4 +19,5 @@ package com.android.systemui.decor abstract class DecorProviderFactory { abstract val providers: List<DecorProvider> abstract val hasProviders: Boolean + abstract fun onDisplayUniqueIdChanged(displayUniqueId: String?) }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt index 9f8679cdea4a..d775ad39a5c3 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt @@ -16,31 +16,25 @@ package com.android.systemui.decor import android.annotation.IdRes -import android.view.DisplayCutout -import android.view.LayoutInflater +import android.content.Context import android.view.Surface import android.view.View import android.view.ViewGroup -import com.android.systemui.R -import java.util.HashMap - -class OverlayWindow(private val layoutInflater: LayoutInflater, private val pos: Int) { - - private val layoutId: Int - get() { - return if (pos == DisplayCutout.BOUNDS_POSITION_LEFT || - pos == DisplayCutout.BOUNDS_POSITION_TOP) { - R.layout.rounded_corners_top - } else { - R.layout.rounded_corners_bottom - } - } +import com.android.systemui.RegionInterceptingFrameLayout + +class OverlayWindow(private val context: Context) { - val rootView = layoutInflater.inflate(layoutId, null) as ViewGroup - private val viewProviderMap: MutableMap<Int, Pair<View, DecorProvider>> = HashMap() + val rootView = RegionInterceptingFrameLayout(context) as ViewGroup + private val viewProviderMap = mutableMapOf<Int, Pair<View, DecorProvider>>() - fun addDecorProvider(decorProvider: DecorProvider, @Surface.Rotation rotation: Int) { - val view = decorProvider.inflateView(layoutInflater, rootView, rotation) + val viewIds: List<Int> + get() = viewProviderMap.keys.toList() + + fun addDecorProvider( + decorProvider: DecorProvider, + @Surface.Rotation rotation: Int + ) { + val view = decorProvider.inflateView(context, rootView, rotation) viewProviderMap[decorProvider.viewId] = Pair(view, decorProvider) } @@ -56,4 +50,54 @@ class OverlayWindow(private val layoutInflater: LayoutInflater, private val pos: viewProviderMap.remove(id) } } + + /** + * Remove views which does not been found in expectExistViewIds + */ + fun removeRedundantViews(expectExistViewIds: IntArray?) { + viewIds.forEach { + if (expectExistViewIds == null || !(expectExistViewIds.contains(it))) { + removeView(it) + } + } + } + + /** + * Check that newProviders is the same list with viewProviderMap. + */ + fun hasSameProviders(newProviders: List<DecorProvider>): Boolean { + return (newProviders.size == viewProviderMap.size) && + newProviders.all { getView(it.viewId) != null } + } + + /** + * Apply new configuration info into views. + * @param filterIds target view ids. Apply to all if null. + * @param rotation current or new rotation direction. + * @param displayUniqueId new displayUniqueId if any. + */ + fun onReloadResAndMeasure( + filterIds: Array<Int>? = null, + reloadToken: Int, + @Surface.Rotation rotation: Int, + displayUniqueId: String? = null + ) { + filterIds?.forEach { id -> + viewProviderMap[id]?.let { + it.second.onReloadResAndMeasure( + view = it.first, + reloadToken = reloadToken, + displayUniqueId = displayUniqueId, + rotation = rotation) + } + } ?: run { + viewProviderMap.values.forEach { + it.second.onReloadResAndMeasure( + view = it.first, + reloadToken = reloadToken, + displayUniqueId = displayUniqueId, + rotation = rotation) + } + } + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt index 7afd7e0eedc5..d16d9604c002 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt @@ -16,6 +16,7 @@ package com.android.systemui.decor +import android.content.Context import android.content.res.Resources import android.view.DisplayCutout import android.view.LayoutInflater @@ -38,6 +39,10 @@ class PrivacyDotDecorProviderFactory @Inject constructor( override val hasProviders: Boolean get() = isPrivacyDotEnabled + override fun onDisplayUniqueIdChanged(displayUniqueId: String?) { + // Do nothing for privacy dot + } + override val providers: List<DecorProvider> get() { return if (hasProviders) { @@ -76,12 +81,21 @@ class PrivacyDotCornerDecorProviderImpl( private val layoutId: Int ) : CornerDecorProvider() { + override fun onReloadResAndMeasure( + view: View, + reloadToken: Int, + rotation: Int, + displayUniqueId: String? + ) { + // Do nothing here because it is handled inside PrivacyDotViewController + } + override fun inflateView( - inflater: LayoutInflater, + context: Context, parent: ViewGroup, @Surface.Rotation rotation: Int ): View { - inflater.inflate(layoutId, parent, true) + LayoutInflater.from(context).inflate(layoutId, parent, true) return parent.getChildAt(parent.childCount - 1 /* latest new added child */) } } diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt new file mode 100644 index 000000000000..a4f7a586ab97 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2022 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.decor + +import android.view.DisplayCutout +import com.android.systemui.R + +class RoundedCornerDecorProviderFactory( + private val roundedCornerResDelegate: RoundedCornerResDelegate +) : DecorProviderFactory() { + + override val hasProviders: Boolean + get() = roundedCornerResDelegate.run { + // We don't consider isMultipleRadius here because it makes no sense if size is zero. + topRoundedSize.width > 0 || bottomRoundedSize.width > 0 + } + + override fun onDisplayUniqueIdChanged(displayUniqueId: String?) { + roundedCornerResDelegate.updateDisplayUniqueId(displayUniqueId, null) + } + + override val providers: List<DecorProvider> + get() { + val hasTop = roundedCornerResDelegate.topRoundedSize.width > 0 + val hasBottom = roundedCornerResDelegate.bottomRoundedSize.width > 0 + return when { + hasTop && hasBottom -> listOf( + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_top_left, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_top_right, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_bottom_left, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_bottom_right, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + roundedCornerResDelegate = roundedCornerResDelegate) + ) + hasTop -> listOf( + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_top_left, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_top_right, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + roundedCornerResDelegate = roundedCornerResDelegate) + ) + hasBottom -> listOf( + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_bottom_left, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_bottom_right, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + roundedCornerResDelegate = roundedCornerResDelegate) + ) + else -> emptyList() + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt new file mode 100644 index 000000000000..90ff950406b4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2022 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.decor + +import android.content.Context +import android.view.DisplayCutout +import android.view.Gravity +import android.view.Surface +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView +import com.android.systemui.R + +class RoundedCornerDecorProviderImpl( + override val viewId: Int, + @DisplayCutout.BoundsPosition override val alignedBound1: Int, + @DisplayCutout.BoundsPosition override val alignedBound2: Int, + private val roundedCornerResDelegate: RoundedCornerResDelegate +) : CornerDecorProvider() { + + private val isTop = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + + override fun inflateView( + context: Context, + parent: ViewGroup, + @Surface.Rotation rotation: Int + ): View { + return ImageView(context).also { view -> + // View + view.id = viewId + initView(view, rotation) + + // LayoutParams + val layoutSize = if (isTop) { + roundedCornerResDelegate.topRoundedSize + } else { + roundedCornerResDelegate.bottomRoundedSize + } + val params = FrameLayout.LayoutParams( + layoutSize.width, + layoutSize.height, + alignedBound1.toLayoutGravity(rotation) or + alignedBound2.toLayoutGravity(rotation)) + + // AddView + parent.addView(view, params) + } + } + + private fun initView(view: ImageView, @Surface.Rotation rotation: Int) { + view.setRoundedCornerImage(roundedCornerResDelegate, isTop) + view.adjustRotation(alignedBounds, rotation) + view.setColorFilter(IMAGE_TINT_COLOR) + } + + override fun onReloadResAndMeasure( + view: View, + reloadToken: Int, + @Surface.Rotation rotation: Int, + displayUniqueId: String? + ) { + roundedCornerResDelegate.updateDisplayUniqueId(displayUniqueId, reloadToken) + + initView((view as ImageView), rotation) + + val layoutSize = if (isTop) { + roundedCornerResDelegate.topRoundedSize + } else { + roundedCornerResDelegate.bottomRoundedSize + } + (view.layoutParams as FrameLayout.LayoutParams).let { + it.width = layoutSize.width + it.height = layoutSize.height + it.gravity = alignedBound1.toLayoutGravity(rotation) or + alignedBound2.toLayoutGravity(rotation) + view.setLayoutParams(it) + } + } +} + +private const val IMAGE_TINT_COLOR: Int = 0xFF000000.toInt() + +@DisplayCutout.BoundsPosition +private fun Int.toLayoutGravity(@Surface.Rotation rotation: Int): Int = when (rotation) { + Surface.ROTATION_0 -> when (this) { + DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.LEFT + DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.TOP + DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.RIGHT + else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.BOTTOM + } + Surface.ROTATION_90 -> when (this) { + DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.BOTTOM + DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.LEFT + DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.TOP + else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.LEFT + } + Surface.ROTATION_270 -> when (this) { + DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.TOP + DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.RIGHT + DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.BOTTOM + else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.LEFT + } + else /* Surface.ROTATION_180 */ -> when (this) { + DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.RIGHT + DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.BOTTOM + DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.LEFT + else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.TOP + } +} + +private fun ImageView.setRoundedCornerImage( + resDelegate: RoundedCornerResDelegate, + isTop: Boolean +) { + val drawable = if (isTop) + resDelegate.topRoundedDrawable + else + resDelegate.bottomRoundedDrawable + + if (drawable != null) { + setImageDrawable(drawable) + } else { + setImageResource( + if (isTop) + R.drawable.rounded_corner_top + else + R.drawable.rounded_corner_bottom + ) + } +} + +/** + * Configures the rounded corner drawable's view matrix based on the gravity. + * + * The gravity describes which corner to configure for, and the drawable we are rotating is assumed + * to be oriented for the top-left corner of the device regardless of the target corner. + * Therefore we need to rotate 180 degrees to get a bottom-left corner, and mirror in the x- or + * y-axis for the top-right and bottom-left corners. + */ +private fun ImageView.adjustRotation(alignedBounds: List<Int>, @Surface.Rotation rotation: Int) { + var newRotation = 0F + var newScaleX = 1F + var newScaleY = 1F + + val isTop = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + val isLeft = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT) + when (rotation) { + Surface.ROTATION_0 -> when { + isTop && isLeft -> {} + isTop && !isLeft -> { newScaleX = -1F } + !isTop && isLeft -> { newScaleY = -1F } + else /* !isTop && !isLeft */ -> { newRotation = 180F } + } + Surface.ROTATION_90 -> when { + isTop && isLeft -> { newScaleY = -1F } + isTop && !isLeft -> {} + !isTop && isLeft -> { newRotation = 180F } + else /* !isTop && !isLeft */ -> { newScaleX = -1F } + } + Surface.ROTATION_270 -> when { + isTop && isLeft -> { newScaleX = -1F } + isTop && !isLeft -> { newRotation = 180F } + !isTop && isLeft -> {} + else /* !isTop && !isLeft */ -> { newScaleY = -1F } + } + else /* Surface.ROTATION_180 */ -> when { + isTop && isLeft -> { newRotation = 180F } + isTop && !isLeft -> { newScaleY = -1F } + !isTop && isLeft -> { newScaleX = -1F } + else /* !isTop && !isLeft */ -> {} + } + } + + this.rotation = newRotation + this.scaleX = newScaleX + this.scaleY = newScaleY +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt index ed175ef7a095..c2bab269d396 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt @@ -35,6 +35,8 @@ class RoundedCornerResDelegate( private val density: Float get() = res.displayMetrics.density + private var reloadToken: Int = 0 + var isMultipleRadius: Boolean = false private set @@ -59,12 +61,26 @@ class RoundedCornerResDelegate( reloadMeasures() } - fun reloadAll(newDisplayUniqueId: String?) { - displayUniqueId = newDisplayUniqueId + private fun reloadAll(newReloadToken: Int) { + if (reloadToken == newReloadToken) { + return + } + reloadToken = newReloadToken reloadRes() reloadMeasures() } + fun updateDisplayUniqueId(newDisplayUniqueId: String?, newReloadToken: Int?) { + if (displayUniqueId != newDisplayUniqueId) { + displayUniqueId = newDisplayUniqueId + newReloadToken ?.let { reloadToken = it } + reloadRes() + reloadMeasures() + } else { + newReloadToken?.let { reloadAll(it) } + } + } + private fun reloadRes() { val configIdx = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId) isMultipleRadius = getIsMultipleRadius(configIdx) @@ -122,7 +138,11 @@ class RoundedCornerResDelegate( } } - fun updateTuningSizeFactor(factor: Int) { + fun updateTuningSizeFactor(factor: Int?, newReloadToken: Int) { + if (reloadToken == newReloadToken) { + return + } + reloadToken = newReloadToken reloadMeasures(factor) } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 0f45a7562d0c..72488f3dc823 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -83,6 +83,7 @@ import android.util.Log; import android.view.Display; import android.view.Gravity; import android.view.HapticFeedbackConstants; +import android.view.InsetsState; import android.view.InsetsState.InternalInsetsType; import android.view.InsetsVisibilities; import android.view.KeyEvent; @@ -1558,10 +1559,12 @@ public class NavigationBar extends ViewController<NavigationBarView> implements | WindowManager.LayoutParams.FLAG_SLIPPERY, PixelFormat.TRANSLUCENT); lp.gravity = gravity; + lp.providedInternalInsets = new Insets[InsetsState.SIZE]; if (insetsHeight != -1) { - lp.providedInternalInsets = Insets.of(0, height - insetsHeight, 0, 0); + lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] = + Insets.of(0, height - insetsHeight, 0, 0); } else { - lp.providedInternalInsets = Insets.NONE; + lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] = null; } lp.token = new Binder(); lp.accessibilityTitle = mContext.getString(R.string.nav_bar); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java index 7e0410c0674b..dd99db49c1d2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java @@ -143,9 +143,10 @@ class QSSecurityFooter extends ViewController<View> @Inject QSSecurityFooter(@Named(QS_SECURITY_FOOTER_VIEW) View rootView, - UserTracker userTracker, @Main Handler mainHandler, ActivityStarter activityStarter, - SecurityController securityController, DialogLaunchAnimator dialogLaunchAnimator, - @Background Looper bgLooper, BroadcastDispatcher broadcastDispatcher) { + UserTracker userTracker, @Main Handler mainHandler, + ActivityStarter activityStarter, SecurityController securityController, + DialogLaunchAnimator dialogLaunchAnimator, @Background Looper bgLooper, + BroadcastDispatcher broadcastDispatcher) { super(rootView); mFooterText = mView.findViewById(R.id.footer_text); mPrimaryFooterIcon = mView.findViewById(R.id.primary_footer_icon); @@ -493,21 +494,23 @@ class QSSecurityFooter extends ViewController<View> private void createDialog() { mShouldUseSettingsButton.set(false); - final View view = createDialogView(); - mMainHandler.post(() -> { - mDialog = new SystemUIDialog(mContext, 0); // Use mContext theme - mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); - mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this); - mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, - mShouldUseSettingsButton.get() ? getSettingsButton() : getNegativeButton(), - this); - - mDialog.setView(view); - if (mView.isAggregatedVisible()) { - mDialogLaunchAnimator.showFromView(mDialog, mView); - } else { - mDialog.show(); - } + mHandler.post(() -> { + String settingsButtonText = getSettingsButton(); + final View view = createDialogView(); + mMainHandler.post(() -> { + mDialog = new SystemUIDialog(mContext, 0); // Use mContext theme + mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this); + mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, mShouldUseSettingsButton.get() + ? settingsButtonText : getNegativeButton(), this); + + mDialog.setView(view); + if (mView.isAggregatedVisible()) { + mDialogLaunchAnimator.showFromView(mDialog, mView); + } else { + mDialog.show(); + } + }); }); } @@ -650,6 +653,7 @@ class QSSecurityFooter extends ViewController<View> } } + // This should not be called on the main thread to avoid making an IPC. @VisibleForTesting String getSettingsButton() { return mDpm.getResources().getString( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 5bf75c796fc9..efb46b9689d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -32,6 +32,7 @@ import android.annotation.Nullable; import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; +import android.app.role.RoleManager; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -380,12 +381,22 @@ public class ExpandableNotificationRow extends ActivatableNotificationView Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 0, USER_SYSTEM) == 1) { INotificationManager iNm = INotificationManager.Stub.asInterface( ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + + boolean isSystem = false; try { - return iNm.isPermissionFixed(sbn.getPackageName(), sbn.getUserId()); + isSystem = iNm.isPermissionFixed(sbn.getPackageName(), sbn.getUserId()); } catch (RemoteException e) { Log.e(TAG, "cannot reach NMS"); } - return false; + RoleManager rm = context.getSystemService(RoleManager.class); + List<String> fixedRoleHolders = new ArrayList<>(); + fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_DIALER)); + fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_EMERGENCY)); + if (fixedRoleHolders.contains(sbn.getPackageName())) { + isSystem = true; + } + + return isSystem; } else { PackageManager packageManager = CentralSurfaces.getPackageManagerForUser( context, sbn.getUser().getIdentifier()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index 59a78ed6f4ea..8b01a4790f3c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -124,6 +124,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private NotificationEntry mEntry; private StatusBarNotification mSbn; private boolean mIsDeviceProvisioned; + private boolean mIsSystemRegisteredCall; private OnSettingsClickListener mOnSettingsClickListener; private OnAppSettingsClickListener mAppSettingsClickListener; @@ -229,6 +230,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mShowAutomaticSetting = mAssistantFeedbackController.isFeedbackEnabled(); mUiEventLogger = uiEventLogger; + mIsSystemRegisteredCall = mSbn.getNotification().isStyle(Notification.CallStyle.class) + && mINotificationManager.isInCall(mSbn.getPackageName(), mSbn.getUid()); + int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage( pkg, mAppUid, false /* includeDeleted */); if (mNumUniqueChannelsInRow == 0) { @@ -252,17 +256,27 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } private void bindInlineControls() { - if (mIsNonblockable) { + if (mIsSystemRegisteredCall) { + findViewById(R.id.non_configurable_call_text).setVisibility(VISIBLE); + findViewById(R.id.non_configurable_text).setVisibility(GONE); + findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE); + findViewById(R.id.interruptiveness_settings).setVisibility(GONE); + ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button); + findViewById(R.id.turn_off_notifications).setVisibility(GONE); + } else if (mIsNonblockable) { findViewById(R.id.non_configurable_text).setVisibility(VISIBLE); + findViewById(R.id.non_configurable_call_text).setVisibility(GONE); findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE); findViewById(R.id.interruptiveness_settings).setVisibility(GONE); ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button); findViewById(R.id.turn_off_notifications).setVisibility(GONE); } else if (mNumUniqueChannelsInRow > 1) { + findViewById(R.id.non_configurable_call_text).setVisibility(GONE); findViewById(R.id.non_configurable_text).setVisibility(GONE); findViewById(R.id.interruptiveness_settings).setVisibility(GONE); findViewById(R.id.non_configurable_multichannel_text).setVisibility(VISIBLE); } else { + findViewById(R.id.non_configurable_call_text).setVisibility(GONE); findViewById(R.id.non_configurable_text).setVisibility(GONE); findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE); findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 26ffdf25318d..290df3e3e1af 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -292,9 +292,7 @@ public class PhoneStatusBarPolicy mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled()); // managed profile - mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status, - getManagedProfileAccessibilityString()); - mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible); + updateManagedProfile(); // data saver mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver, @@ -521,7 +519,7 @@ public class PhoneStatusBarPolicy } private void updateManagedProfile() { - // getLastResumedActivityUserId needds to acquire the AM lock, which may be contended in + // getLastResumedActivityUserId needs to acquire the AM lock, which may be contended in // some cases. Since it doesn't really matter here whether it's updated in this frame // or in the next one, we call this method from our UI offload thread. mUiBgExecutor.execute(() -> { @@ -529,6 +527,7 @@ public class PhoneStatusBarPolicy try { userId = ActivityTaskManager.getService().getLastResumedActivityUserId(); boolean isManagedProfile = mUserManager.isManagedProfile(userId); + String accessibilityString = getManagedProfileAccessibilityString(); mHandler.post(() -> { final boolean showIcon; if (isManagedProfile && (!mKeyguardStateController.isShowing() @@ -536,7 +535,7 @@ public class PhoneStatusBarPolicy showIcon = true; mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status, - getManagedProfileAccessibilityString()); + accessibilityString); } else { showIcon = false; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java index 50bd9b094761..6bb994f4368a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java @@ -27,14 +27,15 @@ import static com.google.common.truth.Truth.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -62,9 +63,11 @@ import android.util.Size; import android.view.Display; import android.view.DisplayCutout; import android.view.View; +import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowMetrics; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; @@ -103,7 +106,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { private SecureSettings mSecureSettings; private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); private FakeThreadFactory mThreadFactory; - private ArrayList<DecorProvider> mDecorProviders; + private ArrayList<DecorProvider> mPrivacyDecorProviders; @Mock private Display mDisplay; @Mock @@ -216,16 +219,43 @@ public class ScreenDecorationsTest extends SysuiTestCase { } } - private void verifyRoundedCornerViewsVisibility( + @NonNull + private int[] getRoundCornerIdsFromOverlayId(@DisplayCutout.BoundsPosition int overlayId) { + switch (overlayId) { + case BOUNDS_POSITION_LEFT: + return new int[] { + R.id.rounded_corner_top_left, + R.id.rounded_corner_top_left }; + case BOUNDS_POSITION_TOP: + return new int[] { + R.id.rounded_corner_top_left, + R.id.rounded_corner_top_right }; + case BOUNDS_POSITION_RIGHT: + return new int[] { + R.id.rounded_corner_top_right, + R.id.rounded_corner_bottom_right }; + case BOUNDS_POSITION_BOTTOM: + return new int[] { + R.id.rounded_corner_bottom_left, + R.id.rounded_corner_bottom_right }; + default: + throw new IllegalArgumentException("unknown overlayId: " + overlayId); + } + } + + private void verifyRoundedCornerViewsExist( @DisplayCutout.BoundsPosition final int overlayId, - @View.Visibility final int visibility) { + @View.Visibility final boolean isExist) { final View overlay = mScreenDecorations.mOverlays[overlayId].getRootView(); - final View left = overlay.findViewById(R.id.left); - final View right = overlay.findViewById(R.id.right); - assertNotNull(left); - assertNotNull(right); - assertThat(left.getVisibility()).isEqualTo(visibility); - assertThat(right.getVisibility()).isEqualTo(visibility); + for (int id: getRoundCornerIdsFromOverlayId(overlayId)) { + final View view = overlay.findViewById(id); + if (isExist) { + assertNotNull(view); + assertThat(view.getVisibility()).isEqualTo(View.VISIBLE); + } else { + assertNull(view); + } + } } @Nullable @@ -388,8 +418,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.mPrivacyDotShowingListener); // Rounded corner views shall not exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false); // Privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -417,8 +447,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Privacy dots shall not exist verifyDotViewsNullable(true); @@ -447,8 +477,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verify(mDotViewController, times(1)).setShowingListener(null); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -488,21 +518,26 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.start(); View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView() - .findViewById(R.id.left); + .findViewById(R.id.rounded_corner_top_left); View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView() - .findViewById(R.id.right); - verify(mScreenDecorations, atLeastOnce()) - .setSize(leftRoundedCorner, new Size(testTopRadius, testTopRadius)); - verify(mScreenDecorations, atLeastOnce()) - .setSize(rightRoundedCorner, new Size(testTopRadius, testTopRadius)); + .findViewById(R.id.rounded_corner_top_right); + ViewGroup.LayoutParams leftParams = leftRoundedCorner.getLayoutParams(); + ViewGroup.LayoutParams rightParams = rightRoundedCorner.getLayoutParams(); + assertEquals(leftParams.width, testTopRadius); + assertEquals(leftParams.height, testTopRadius); + assertEquals(rightParams.width, testTopRadius); + assertEquals(rightParams.height, testTopRadius); + leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView() - .findViewById(R.id.left); + .findViewById(R.id.rounded_corner_bottom_left); rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView() - .findViewById(R.id.right); - verify(mScreenDecorations, atLeastOnce()) - .setSize(leftRoundedCorner, new Size(testBottomRadius, testBottomRadius)); - verify(mScreenDecorations, atLeastOnce()) - .setSize(rightRoundedCorner, new Size(testBottomRadius, testBottomRadius)); + .findViewById(R.id.rounded_corner_bottom_right); + leftParams = leftRoundedCorner.getLayoutParams(); + rightParams = rightRoundedCorner.getLayoutParams(); + assertEquals(leftParams.width, testBottomRadius); + assertEquals(leftParams.height, testBottomRadius); + assertEquals(rightParams.width, testBottomRadius); + assertEquals(rightParams.height, testBottomRadius); } @Test @@ -518,31 +553,27 @@ public class ScreenDecorationsTest extends SysuiTestCase { .when(mScreenDecorations).getCutout(); mScreenDecorations.start(); - final Size topRadius = new Size(testTopRadius, testTopRadius); - final Size bottomRadius = new Size(testBottomRadius, testBottomRadius); - View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView() - .findViewById(R.id.left); - boolean isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_LEFT, R.id.left); - verify(mScreenDecorations, atLeastOnce()) - .setSize(leftRoundedCorner, isTop ? topRadius : bottomRadius); - - View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView() - .findViewById(R.id.right); - isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_LEFT, R.id.right); - verify(mScreenDecorations, atLeastOnce()) - .setSize(rightRoundedCorner, isTop ? topRadius : bottomRadius); - - leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView() - .findViewById(R.id.left); - isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_RIGHT, R.id.left); - verify(mScreenDecorations, atLeastOnce()) - .setSize(leftRoundedCorner, isTop ? topRadius : bottomRadius); - - rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView() - .findViewById(R.id.right); - isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_RIGHT, R.id.right); - verify(mScreenDecorations, atLeastOnce()) - .setSize(rightRoundedCorner, isTop ? topRadius : bottomRadius); + View topRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView() + .findViewById(R.id.rounded_corner_top_left); + View bottomRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView() + .findViewById(R.id.rounded_corner_bottom_left); + ViewGroup.LayoutParams topParams = topRoundedCorner.getLayoutParams(); + ViewGroup.LayoutParams bottomParams = bottomRoundedCorner.getLayoutParams(); + assertEquals(topParams.width, testTopRadius); + assertEquals(topParams.height, testTopRadius); + assertEquals(bottomParams.width, testBottomRadius); + assertEquals(bottomParams.height, testBottomRadius); + + topRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView() + .findViewById(R.id.rounded_corner_top_right); + bottomRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView() + .findViewById(R.id.rounded_corner_bottom_right); + topParams = topRoundedCorner.getLayoutParams(); + bottomParams = bottomRoundedCorner.getLayoutParams(); + assertEquals(topParams.width, testTopRadius); + assertEquals(topParams.height, testTopRadius); + assertEquals(bottomParams.width, testBottomRadius); + assertEquals(bottomParams.height, testBottomRadius); } @Test @@ -562,8 +593,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Privacy dots shall not exist verifyDotViewsNullable(true); @@ -598,8 +629,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verify(mDotViewController, times(1)).setShowingListener(null); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -660,10 +691,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Top rounded corner views shall exist because of cutout // but be gone because of no rounded corner - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false); // Bottom rounded corner views shall exist because of privacy dot // but be gone because of no rounded corner - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false); // Privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -691,7 +722,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Left rounded corner views shall exist because of cutout // but be gone because of no rounded corner - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_LEFT, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_LEFT, false); // Top privacy dots shall not exist because of no privacy verifyDotViewsNullable(true); @@ -745,8 +776,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Top privacy dots shall not exist because of no privacy dot verifyDotViewsNullable(true); @@ -775,8 +806,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verify(mDotViewController, times(1)).setShowingListener(null); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Top privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -914,7 +945,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { verify(mDotViewController, times(2)).setShowingListener(null); // Verify each privacy dot id appears only once - mDecorProviders.stream().map(DecorProvider::getViewId).forEach(viewId -> { + mPrivacyDecorProviders.stream().map(DecorProvider::getViewId).forEach(viewId -> { int findCount = 0; for (OverlayWindow overlay: mScreenDecorations.mOverlays) { if (overlay == null) { @@ -968,8 +999,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Both top and bottom windows should be added with INVISIBLE because of only privacy dot, // but rounded corners visibility shall be gone because of no rounding. verifyOverlaysExistAndAdded(false, true, false, true, View.INVISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false); verify(mDotViewController, times(1)).initialize(any(), any(), any(), any()); verify(mDotViewController, times(1)).setShowingListener( mScreenDecorations.mPrivacyDotShowingListener); @@ -982,8 +1013,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Both top and bottom windows should be added with VISIBLE because of privacy dot and // cutout, but rounded corners visibility shall be gone because of no rounding. verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false); verify(mDotViewController, times(2)).initialize(any(), any(), any(), any()); verify(mDotViewController, times(1)).setShowingListener(null); } @@ -1297,6 +1328,48 @@ public class ScreenDecorationsTest extends SysuiTestCase { verify(cutoutView, times(1)).onDisplayChanged(1); } + @Test + public void testHasSameProvidersWithNullOverlays() { + setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, + 0 /* roundedPadding */, false /* multipleRadius */, + false /* fillCutout */, false /* privacyDot */); + + mScreenDecorations.start(); + + final ArrayList<DecorProvider> newProviders = new ArrayList<>(); + assertTrue(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotTopLeftDecorProvider); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotTopRightDecorProvider); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + } + + @Test + public void testHasSameProvidersWithPrivacyDots() { + setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, + 0 /* roundedPadding */, false /* multipleRadius */, + true /* fillCutout */, true /* privacyDot */); + + mScreenDecorations.start(); + + final ArrayList<DecorProvider> newProviders = new ArrayList<>(); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotTopLeftDecorProvider); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotTopRightDecorProvider); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotBottomLeftDecorProvider); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotBottomRightDecorProvider); + assertTrue(mScreenDecorations.hasSameProviders(newProviders)); + } + private void setupResources(int radius, int radiusTop, int radiusBottom, int roundedPadding, boolean multipleRadius, boolean fillCutout, boolean privacyDot) { mContext.getOrCreateTestableResources().addOverride( @@ -1336,14 +1409,14 @@ public class ScreenDecorationsTest extends SysuiTestCase { mContext.getOrCreateTestableResources().addOverride( com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, fillCutout); - mDecorProviders = new ArrayList<>(); + mPrivacyDecorProviders = new ArrayList<>(); if (privacyDot) { - mDecorProviders.add(mPrivacyDotTopLeftDecorProvider); - mDecorProviders.add(mPrivacyDotTopRightDecorProvider); - mDecorProviders.add(mPrivacyDotBottomLeftDecorProvider); - mDecorProviders.add(mPrivacyDotBottomRightDecorProvider); + mPrivacyDecorProviders.add(mPrivacyDotTopLeftDecorProvider); + mPrivacyDecorProviders.add(mPrivacyDotTopRightDecorProvider); + mPrivacyDecorProviders.add(mPrivacyDotBottomLeftDecorProvider); + mPrivacyDecorProviders.add(mPrivacyDotBottomRightDecorProvider); } - when(mPrivacyDotDecorProviderFactory.getProviders()).thenReturn(mDecorProviders); + when(mPrivacyDotDecorProviderFactory.getProviders()).thenReturn(mPrivacyDecorProviders); when(mPrivacyDotDecorProviderFactory.getHasProviders()).thenReturn(privacyDot); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index 666c9e481adc..2341928b2565 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -40,6 +40,9 @@ import com.android.internal.widget.LockPatternUtils import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.WakefulnessLifecycle +import com.android.systemui.util.concurrency.DelayableExecutor +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Rule @@ -314,7 +317,8 @@ class AuthContainerViewTest : SysuiTestCase() { wakefulnessLifecycle, userManager, lockPatternUtils, - Handler(TestableLooper.get(this).looper) + Handler(TestableLooper.get(this).looper), + FakeExecutor(FakeSystemClock()) ) if (addToView) { @@ -331,10 +335,11 @@ class AuthContainerViewTest : SysuiTestCase() { wakefulnessLifecycle: WakefulnessLifecycle, userManager: UserManager, lockPatternUtils: LockPatternUtils, - mainHandler: Handler + mainHandler: Handler, + bgExecutor: DelayableExecutor ) : AuthContainerView( config, fpProps, faceProps, - wakefulnessLifecycle, userManager, lockPatternUtils, mainHandler + wakefulnessLifecycle, userManager, lockPatternUtils, mainHandler, bgExecutor ) { override fun postOnAnimation(runnable: Runnable) { runnable.run() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index 190228d80cde..4858ab5234f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -80,8 +80,11 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.Execution; import com.android.systemui.util.concurrency.FakeExecution; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Rule; @@ -156,6 +159,7 @@ public class AuthControllerTest extends SysuiTestCase { private Execution mExecution; private TestableLooper mTestableLooper; private Handler mHandler; + private DelayableExecutor mBackgroundExecutor; private TestableAuthController mAuthController; @Before @@ -164,6 +168,7 @@ public class AuthControllerTest extends SysuiTestCase { mExecution = new FakeExecution(); mTestableLooper = TestableLooper.get(this); mHandler = new Handler(mTestableLooper.getLooper()); + mBackgroundExecutor = new FakeExecutor(new FakeSystemClock()); when(mContextSpy.getPackageManager()).thenReturn(mPackageManager); when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) @@ -759,11 +764,12 @@ public class AuthControllerTest extends SysuiTestCase { super(context, execution, commandQueue, activityTaskManager, windowManager, fingerprintManager, faceManager, udfpsControllerFactory, sidefpsControllerFactory, mDisplayManager, mWakefulnessLifecycle, - mUserManager, mLockPatternUtils, statusBarStateController, mHandler); + mUserManager, mLockPatternUtils, statusBarStateController, mHandler, + mBackgroundExecutor); } @Override - protected AuthDialog buildDialog(PromptInfo promptInfo, + protected AuthDialog buildDialog(DelayableExecutor bgExecutor, PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds, String opPackageName, boolean skipIntro, long operationId, long requestId, @BiometricManager.BiometricMultiSensorMode int multiSensorConfig, diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt index ca74df0a23c5..69366fa0d4a9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt @@ -19,25 +19,19 @@ package com.android.systemui.decor import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.DisplayCutout -import android.view.LayoutInflater import android.view.Surface import android.view.View -import android.view.ViewGroup import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase -import com.android.systemui.util.mockito.eq import org.junit.Assert import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.Mockito.anyInt +import org.mockito.Mockito.never import org.mockito.Mockito.spy +import org.mockito.Mockito.times import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations -import org.mockito.Mockito.`when` as whenever @RunWith(AndroidTestingRunner::class) @RunWithLooper(setAsMainLooper = true) @@ -45,62 +39,144 @@ import org.mockito.Mockito.`when` as whenever class OverlayWindowTest : SysuiTestCase() { companion object { - private val TEST_DECOR_VIEW_ID = R.id.privacy_dot_bottom_right_container - private val TEST_DECOR_LAYOUT_ID = R.layout.privacy_dot_bottom_right + private val TEST_DECOR_VIEW_ID_1 = R.id.privacy_dot_top_left_container + private val TEST_DECOR_VIEW_ID_2 = R.id.privacy_dot_bottom_left_container + private val TEST_DECOR_VIEW_ID_3 = R.id.privacy_dot_bottom_right_container } private lateinit var overlay: OverlayWindow - - @Mock private lateinit var layoutInflater: LayoutInflater - @Mock private lateinit var decorProvider: DecorProvider + private lateinit var decorProvider1: DecorProvider + private lateinit var decorProvider2: DecorProvider + private lateinit var decorProvider3: DecorProvider @Before fun setUp() { - MockitoAnnotations.initMocks(this) - - layoutInflater = spy(LayoutInflater.from(mContext)) - - overlay = OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_RIGHT) - - whenever(decorProvider.viewId).thenReturn(TEST_DECOR_VIEW_ID) - whenever(decorProvider.inflateView( - eq(layoutInflater), - eq(overlay.rootView), - anyInt()) - ).then { - val layoutInflater = it.getArgument<LayoutInflater>(0) - val parent = it.getArgument<ViewGroup>(1) - layoutInflater.inflate(TEST_DECOR_LAYOUT_ID, parent) - return@then parent.getChildAt(parent.childCount - 1) - } - } + decorProvider1 = spy(PrivacyDotCornerDecorProviderImpl( + viewId = TEST_DECOR_VIEW_ID_1, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + layoutId = R.layout.privacy_dot_top_left)) + decorProvider2 = spy(PrivacyDotCornerDecorProviderImpl( + viewId = TEST_DECOR_VIEW_ID_2, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + layoutId = R.layout.privacy_dot_bottom_left)) + decorProvider3 = spy(PrivacyDotCornerDecorProviderImpl( + viewId = TEST_DECOR_VIEW_ID_3, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + layoutId = R.layout.privacy_dot_bottom_right)) - @Test - fun testAnyBoundsPositionShallNoExceptionForConstructor() { - OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_LEFT) - OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_TOP) - OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_RIGHT) - OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_BOTTOM) + overlay = OverlayWindow(mContext) } @Test fun testAddProvider() { @Surface.Rotation val rotation = Surface.ROTATION_270 - overlay.addDecorProvider(decorProvider, rotation) - verify(decorProvider, Mockito.times(1)).inflateView( - eq(layoutInflater), eq(overlay.rootView), eq(rotation)) - val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID) - Assert.assertNotNull(viewFoundFromRootView) - Assert.assertEquals(viewFoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID)) + overlay.addDecorProvider(decorProvider1, rotation) + overlay.addDecorProvider(decorProvider2, rotation) + + verify(decorProvider1, times(1)).inflateView( + mContext, overlay.rootView, rotation) + verify(decorProvider2, times(1)).inflateView( + mContext, overlay.rootView, rotation) + + val view1FoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID_1) + Assert.assertNotNull(view1FoundFromRootView) + Assert.assertEquals(view1FoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID_1)) + val view2FoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID_2) + Assert.assertNotNull(view2FoundFromRootView) + Assert.assertEquals(view2FoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID_2)) } @Test fun testRemoveView() { - @Surface.Rotation val rotation = Surface.ROTATION_270 - overlay.addDecorProvider(decorProvider, rotation) - overlay.removeView(TEST_DECOR_VIEW_ID) - val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID) + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270) + overlay.removeView(TEST_DECOR_VIEW_ID_1) + + val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID_1) Assert.assertNull(viewFoundFromRootView) - Assert.assertNull(overlay.getView(TEST_DECOR_LAYOUT_ID)) + Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_1)) + } + + @Test + fun testOnReloadResAndMeasureWithoutIds() { + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0) + + overlay.onReloadResAndMeasure( + reloadToken = 1, + rotation = Surface.ROTATION_90, + displayUniqueId = null) + verify(decorProvider1, times(1)).onReloadResAndMeasure( + overlay.getView(TEST_DECOR_VIEW_ID_1)!!, 1, Surface.ROTATION_90, null) + verify(decorProvider2, times(1)).onReloadResAndMeasure( + overlay.getView(TEST_DECOR_VIEW_ID_2)!!, 1, Surface.ROTATION_90, null) + } + + @Test + fun testOnReloadResAndMeasureWithIds() { + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0) + + overlay.onReloadResAndMeasure( + filterIds = arrayOf(TEST_DECOR_VIEW_ID_2), + reloadToken = 1, + rotation = Surface.ROTATION_90, + displayUniqueId = null) + verify(decorProvider1, never()).onReloadResAndMeasure( + overlay.getView(TEST_DECOR_VIEW_ID_1)!!, 1, Surface.ROTATION_90, null) + verify(decorProvider2, times(1)).onReloadResAndMeasure( + overlay.getView(TEST_DECOR_VIEW_ID_2)!!, 1, Surface.ROTATION_90, null) + } + + @Test + fun testRemoveRedundantViewsWithNullParameter() { + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270) + + overlay.removeRedundantViews(null) + + Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_1)) + Assert.assertNull(overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_1)) + Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_2)) + Assert.assertNull(overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_2)) + } + + @Test + fun testRemoveRedundantViewsWith2Providers() { + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270) + + overlay.removeRedundantViews(IntArray(2).apply { + this[0] = TEST_DECOR_VIEW_ID_3 + this[1] = TEST_DECOR_VIEW_ID_1 + }) + + Assert.assertNotNull(overlay.getView(TEST_DECOR_VIEW_ID_1)) + Assert.assertNotNull(overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_1)) + Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_2)) + Assert.assertNull(overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_2)) + } + + @Test + fun testHasSameProviders() { + Assert.assertTrue(overlay.hasSameProviders(emptyList())) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider1))) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2))) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2, decorProvider1))) + + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0) + Assert.assertFalse(overlay.hasSameProviders(emptyList())) + Assert.assertTrue(overlay.hasSameProviders(listOf(decorProvider1))) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2))) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2, decorProvider1))) + + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0) + Assert.assertFalse(overlay.hasSameProviders(emptyList())) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider1))) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2))) + Assert.assertTrue(overlay.hasSameProviders(listOf(decorProvider2, decorProvider1))) } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt index bac08176d2eb..171b76748d26 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.decor import android.content.res.Resources import android.testing.AndroidTestingRunner -import android.testing.TestableLooper.RunWithLooper import android.view.DisplayCutout import androidx.test.filters.SmallTest import com.android.systemui.R @@ -32,7 +31,6 @@ import org.mockito.Mockito.spy import org.mockito.Mockito.`when` as whenever @RunWith(AndroidTestingRunner::class) -@RunWithLooper(setAsMainLooper = true) @SmallTest class PrivacyDotDecorProviderFactoryTest : SysuiTestCase() { private lateinit var mPrivacyDotDecorProviderFactory: PrivacyDotDecorProviderFactory diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt new file mode 100644 index 000000000000..621bcf69bb03 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2022 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.decor + +import android.testing.AndroidTestingRunner +import android.util.Size +import android.view.DisplayCutout +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.spy + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class RoundedCornerDecorProviderFactoryTest : SysuiTestCase() { + + @Mock private lateinit var roundedCornerResDelegate: RoundedCornerResDelegate + private lateinit var roundedCornerDecorProviderFactory: RoundedCornerDecorProviderFactory + + @Before + fun setUp() { + roundedCornerResDelegate = spy(RoundedCornerResDelegate(mContext.resources, null)) + } + + @Test + fun testNoRoundedCorners() { + Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).topRoundedSize + Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).bottomRoundedSize + Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius + + roundedCornerDecorProviderFactory = + RoundedCornerDecorProviderFactory(roundedCornerResDelegate) + + Assert.assertEquals(false, roundedCornerDecorProviderFactory.hasProviders) + Assert.assertEquals(0, roundedCornerDecorProviderFactory.providers.size) + } + + @Test + fun testHasRoundedCornersIfTopWidthLargerThan0() { + Mockito.doReturn(Size(1, 0)).`when`(roundedCornerResDelegate).topRoundedSize + Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).bottomRoundedSize + Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius + + roundedCornerDecorProviderFactory = + RoundedCornerDecorProviderFactory(roundedCornerResDelegate) + + Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders) + roundedCornerDecorProviderFactory.providers.let { providers -> + Assert.assertEquals(2, providers.size) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_top_left) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_top_right) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) + }) + } + } + + @Test + fun testHasRoundedCornersIfBottomWidthLargerThan0() { + Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).topRoundedSize + Mockito.doReturn(Size(1, 1)).`when`(roundedCornerResDelegate).bottomRoundedSize + Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius + + roundedCornerDecorProviderFactory = + RoundedCornerDecorProviderFactory(roundedCornerResDelegate) + + Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders) + roundedCornerDecorProviderFactory.providers.let { providers -> + Assert.assertEquals(2, providers.size) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_bottom_left) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_bottom_right) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) + }) + } + } + + @Test + fun test4CornerDecorProvidersInfo() { + Mockito.doReturn(Size(10, 10)).`when`(roundedCornerResDelegate).topRoundedSize + Mockito.doReturn(Size(10, 10)).`when`(roundedCornerResDelegate).bottomRoundedSize + Mockito.doReturn(true).`when`(roundedCornerResDelegate).isMultipleRadius + + roundedCornerDecorProviderFactory = + RoundedCornerDecorProviderFactory(roundedCornerResDelegate) + + Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders) + roundedCornerDecorProviderFactory.providers.let { providers -> + Assert.assertEquals(4, providers.size) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_top_left) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_top_right) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_bottom_left) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_bottom_right) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) + }) + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt index 2effaec58a86..1fec38018f51 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt @@ -45,7 +45,7 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { } @Test - fun testReloadAllAndDefaultRadius() { + fun testUpdateDisplayUniqueId() { mContext.orCreateTestableResources.addOverrides( mockTypeArray = mockTypedArray, radius = 3, @@ -65,7 +65,34 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { radiusTop = 6, radiusBottom = 0) - roundedCornerResDelegate.reloadAll("test") + roundedCornerResDelegate.updateDisplayUniqueId("test", null) + + assertEquals(Size(6, 6), roundedCornerResDelegate.topRoundedSize) + assertEquals(Size(5, 5), roundedCornerResDelegate.bottomRoundedSize) + } + + @Test + fun testNotUpdateDisplayUniqueIdButChangeRefreshToken() { + mContext.orCreateTestableResources.addOverrides( + mockTypeArray = mockTypedArray, + radius = 3, + radiusTop = 0, + radiusBottom = 4, + multipleRadius = false) + + roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) + + assertEquals(Size(3, 3), roundedCornerResDelegate.topRoundedSize) + assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize) + assertEquals(false, roundedCornerResDelegate.isMultipleRadius) + + mContext.orCreateTestableResources.addOverrides( + mockTypeArray = mockTypedArray, + radius = 5, + radiusTop = 6, + radiusBottom = 0) + + roundedCornerResDelegate.updateDisplayUniqueId(null, 1) assertEquals(Size(6, 6), roundedCornerResDelegate.topRoundedSize) assertEquals(Size(5, 5), roundedCornerResDelegate.bottomRoundedSize) @@ -82,11 +109,21 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) val factor = 5 - roundedCornerResDelegate.updateTuningSizeFactor(factor) + roundedCornerResDelegate.updateTuningSizeFactor(factor, 1) val length = (factor * mContext.resources.displayMetrics.density).toInt() assertEquals(Size(length, length), roundedCornerResDelegate.topRoundedSize) assertEquals(Size(length, length), roundedCornerResDelegate.bottomRoundedSize) + + mContext.orCreateTestableResources.addOverrides( + mockTypeArray = mockTypedArray, + radiusTop = 1, + radiusBottom = 2, + multipleRadius = false) + roundedCornerResDelegate.updateTuningSizeFactor(null, 2) + + assertEquals(Size(1, 1), roundedCornerResDelegate.topRoundedSize) + assertEquals(Size(2, 2), roundedCornerResDelegate.bottomRoundedSize) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index 324f0ac08fad..b1f10751119e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -44,12 +44,15 @@ import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; +import android.app.PendingIntent; +import android.app.Person; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.service.notification.StatusBarNotification; +import android.telecom.TelecomManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -116,11 +119,15 @@ public class NotificationInfoTest extends SysuiTestCase { private ChannelEditorDialogController mChannelEditorDialogController; @Mock private AssistantFeedbackController mAssistantFeedbackController; + @Mock + private TelecomManager mTelecomManager; @Before public void setUp() throws Exception { mTestableLooper = TestableLooper.get(this); + mContext.addMockSystemService(TelecomManager.class, mTelecomManager); + mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper()); mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger); // Inflate the layout @@ -161,7 +168,7 @@ public class NotificationInfoTest extends SysuiTestCase { IMPORTANCE_LOW); mDefaultNotificationChannelSet.add(mDefaultNotificationChannel); mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, - new Notification(), UserHandle.CURRENT, null, 0); + new Notification(), UserHandle.getUserHandleForUid(TEST_UID), null, 0); mEntry = new NotificationEntryBuilder().setSbn(mSbn).build(); when(mAssistantFeedbackController.isFeedbackEnabled()).thenReturn(false); when(mAssistantFeedbackController.getInlineDescriptionResource(any())) @@ -632,6 +639,92 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test + public void testBindNotification_whenCurrentlyInCall() throws Exception { + when(mMockINotificationManager.isInCall(anyString(), anyInt())).thenReturn(true); + + Person person = new Person.Builder() + .setName("caller") + .build(); + Notification.Builder nb = new Notification.Builder( + mContext, mNotificationChannel.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setStyle(Notification.CallStyle.forOngoingCall( + person, mock(PendingIntent.class))) + .setFullScreenIntent(mock(PendingIntent.class), true) + .addAction(new Notification.Action.Builder(null, "test", null).build()); + + mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, + nb.build(), UserHandle.getUserHandleForUid(TEST_UID), null, 0); + mEntry.setSbn(mSbn); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mOnUserInteractionCallback, + mChannelEditorDialogController, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + null, + null, + mUiEventLogger, + true, + false, + true, + mAssistantFeedbackController); + final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_call_text); + assertEquals(View.VISIBLE, view.getVisibility()); + assertEquals(mContext.getString(R.string.notification_unblockable_call_desc), + view.getText()); + assertEquals(GONE, + mNotificationInfo.findViewById(R.id.interruptiveness_settings).getVisibility()); + assertEquals(GONE, + mNotificationInfo.findViewById(R.id.non_configurable_text).getVisibility()); + } + + @Test + public void testBindNotification_whenCurrentlyInCall_notCall() throws Exception { + when(mMockINotificationManager.isInCall(anyString(), anyInt())).thenReturn(true); + + Person person = new Person.Builder() + .setName("caller") + .build(); + Notification.Builder nb = new Notification.Builder( + mContext, mNotificationChannel.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setFullScreenIntent(mock(PendingIntent.class), true) + .addAction(new Notification.Action.Builder(null, "test", null).build()); + + mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, + nb.build(), UserHandle.getUserHandleForUid(TEST_UID), null, 0); + mEntry.setSbn(mSbn); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mOnUserInteractionCallback, + mChannelEditorDialogController, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + null, + null, + mUiEventLogger, + true, + false, + true, + mAssistantFeedbackController); + assertEquals(GONE, + mNotificationInfo.findViewById(R.id.non_configurable_call_text).getVisibility()); + assertEquals(VISIBLE, + mNotificationInfo.findViewById(R.id.interruptiveness_settings).getVisibility()); + assertEquals(GONE, + mNotificationInfo.findViewById(R.id.non_configurable_text).getVisibility()); + } + + @Test public void testBindNotification_automaticIsVisible() throws Exception { when(mAssistantFeedbackController.isFeedbackEnabled()).thenReturn(true); mNotificationInfo.bindNotification( diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 2d328d8b0949..210532a88a8c 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -153,7 +153,7 @@ import java.util.concurrent.TimeUnit; public class VcnManagementService extends IVcnManagementService.Stub { @NonNull private static final String TAG = VcnManagementService.class.getSimpleName(); private static final long DUMP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(5); - private static final int LOCAL_LOG_LINE_COUNT = 128; + private static final int LOCAL_LOG_LINE_COUNT = 512; // Public for use in all other VCN classes @NonNull public static final LocalLog LOCAL_LOG = new LocalLog(LOCAL_LOG_LINE_COUNT); @@ -456,7 +456,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { synchronized (mLock) { final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot; mLastSnapshot = snapshot; - logDbg("new snapshot: " + mLastSnapshot); + logInfo("new snapshot: " + mLastSnapshot); // Start any VCN instances as necessary for (Entry<ParcelUuid, VcnConfig> entry : mConfigs.entrySet()) { @@ -522,6 +522,8 @@ public class VcnManagementService extends IVcnManagementService.Stub { @GuardedBy("mLock") private void stopVcnLocked(@NonNull ParcelUuid uuidToTeardown) { + logInfo("Stopping VCN config for subGrp: " + uuidToTeardown); + // Remove in 2 steps. Make sure teardownAsync is triggered before removing from the map. final Vcn vcnToTeardown = mVcns.get(uuidToTeardown); if (vcnToTeardown == null) { @@ -567,7 +569,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { @GuardedBy("mLock") private void startVcnLocked(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) { - logDbg("Starting VCN config for subGrp: " + subscriptionGroup); + logInfo("Starting VCN config for subGrp: " + subscriptionGroup); // TODO(b/193687515): Support multiple VCNs active at the same time if (!mVcns.isEmpty()) { @@ -626,7 +628,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { if (!config.getProvisioningPackageName().equals(opPkgName)) { throw new IllegalArgumentException("Mismatched caller and VcnConfig creator"); } - logDbg("VCN config updated for subGrp: " + subscriptionGroup); + logInfo("VCN config updated for subGrp: " + subscriptionGroup); mContext.getSystemService(AppOpsManager.class) .checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName()); @@ -652,7 +654,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull String opPkgName) { requireNonNull(subscriptionGroup, "subscriptionGroup was null"); requireNonNull(opPkgName, "opPkgName was null"); - logDbg("VCN config cleared for subGrp: " + subscriptionGroup); + logInfo("VCN config cleared for subGrp: " + subscriptionGroup); mContext.getSystemService(AppOpsManager.class) .checkPackage(mDeps.getBinderCallingUid(), opPkgName); @@ -1050,24 +1052,34 @@ public class VcnManagementService extends IVcnManagementService.Stub { Slog.d(TAG, msg, tr); } + private void logInfo(String msg) { + Slog.i(TAG, msg); + LOCAL_LOG.log("[INFO] [" + TAG + "] " + msg); + } + + private void logInfo(String msg, Throwable tr) { + Slog.i(TAG, msg, tr); + LOCAL_LOG.log("[INFO] [" + TAG + "] " + msg + tr); + } + private void logErr(String msg) { Slog.e(TAG, msg); - LOCAL_LOG.log(TAG + " ERR: " + msg); + LOCAL_LOG.log("[ERR] [" + TAG + "] " + msg); } private void logErr(String msg, Throwable tr) { Slog.e(TAG, msg, tr); - LOCAL_LOG.log(TAG + " ERR: " + msg + tr); + LOCAL_LOG.log("[ERR ] [" + TAG + "] " + msg + tr); } private void logWtf(String msg) { Slog.wtf(TAG, msg); - LOCAL_LOG.log(TAG + " WTF: " + msg); + LOCAL_LOG.log("[WTF] [" + TAG + "] " + msg); } private void logWtf(String msg, Throwable tr) { Slog.wtf(TAG, msg, tr); - LOCAL_LOG.log(TAG + " WTF: " + msg + tr); + LOCAL_LOG.log("[WTF ] [" + TAG + "] " + msg + tr); } /** diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index b886196755ea..c04377389e8e 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -173,7 +173,7 @@ class UserController implements Handler.Callback { // Message constant to clear {@link UserJourneySession} from {@link mUserIdToUserJourneyMap} if // the user journey, defined in the UserLifecycleJourneyReported atom for statsd, is not // complete within {@link USER_JOURNEY_TIMEOUT}. - private static final int CLEAR_USER_JOURNEY_SESSION_MSG = 200; + static final int CLEAR_USER_JOURNEY_SESSION_MSG = 200; // Wait time for completing the user journey. If a user journey is not complete within this // time, the remaining lifecycle events for the journey would not be logged in statsd. // Timeout set for 90 seconds. @@ -209,12 +209,15 @@ class UserController implements Handler.Callback { FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_START; private static final int USER_JOURNEY_USER_CREATE = FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE; + private static final int USER_JOURNEY_USER_STOP = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_STOP; @IntDef(prefix = { "USER_JOURNEY" }, value = { USER_JOURNEY_UNKNOWN, USER_JOURNEY_USER_SWITCH_FG, USER_JOURNEY_USER_SWITCH_UI, USER_JOURNEY_USER_START, USER_JOURNEY_USER_CREATE, + USER_JOURNEY_USER_STOP }) @interface UserJourney {} @@ -233,6 +236,8 @@ class UserController implements Handler.Callback { FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKING_USER; private static final int USER_LIFECYCLE_EVENT_UNLOCKED_USER = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKED_USER; + private static final int USER_LIFECYCLE_EVENT_STOP_USER = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__STOP_USER; @IntDef(prefix = { "USER_LIFECYCLE_EVENT" }, value = { USER_LIFECYCLE_EVENT_UNKNOWN, USER_LIFECYCLE_EVENT_SWITCH_USER, @@ -241,6 +246,7 @@ class UserController implements Handler.Callback { USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED, USER_LIFECYCLE_EVENT_UNLOCKING_USER, USER_LIFECYCLE_EVENT_UNLOCKED_USER, + USER_LIFECYCLE_EVENT_STOP_USER }) @interface UserLifecycleEvent {} @@ -1008,6 +1014,10 @@ class UserController implements Handler.Callback { return; } + logUserJourneyInfo(null, getUserInfo(userId), USER_JOURNEY_USER_STOP); + logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, + USER_LIFECYCLE_EVENT_STATE_BEGIN); + if (stopUserCallback != null) { uss.mStopCallbacks.add(stopUserCallback); } @@ -1066,6 +1076,9 @@ class UserController implements Handler.Callback { synchronized (mLock) { if (uss.state != UserState.STATE_STOPPING) { // Whoops, we are being started back up. Abort, abort! + logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, + USER_LIFECYCLE_EVENT_STATE_NONE); + clearSessionId(userId); return; } uss.setState(UserState.STATE_SHUTDOWN); @@ -1165,10 +1178,18 @@ class UserController implements Handler.Callback { mInjector.getUserManager().removeUserEvenWhenDisallowed(userId); } + logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, + USER_LIFECYCLE_EVENT_STATE_FINISH); + clearSessionId(userId); + if (!lockUser) { return; } dispatchUserLocking(userIdToLock, keyEvictedCallbacks); + } else { + logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, + USER_LIFECYCLE_EVENT_STATE_NONE); + clearSessionId(userId); } } @@ -2962,13 +2983,13 @@ class UserController implements Handler.Callback { if (userJourneySession != null) { // TODO(b/157007231): Move this logic to a separate class/file. if ((userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_UI - && journey == USER_JOURNEY_USER_START) - || (userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_FG - && journey == USER_JOURNEY_USER_START)) { + || userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_FG) + && (journey == USER_JOURNEY_USER_START + || journey == USER_JOURNEY_USER_STOP)) { /* - * There is already a user switch journey, and a user start journey for the same - * target user received. User start journey is most likely a part of user switch - * journey so no need to create a new journey for user start. + * There is already a user switch journey, and a user start or stop journey for + * the same target user received. New journey is most likely a part of user + * switch journey so no need to create a new journey. */ if (DEBUG_MU) { Slogf.d(TAG, journey + " not logged as it is expected to be part of " diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 465e5e9d8453..b1b5d3ffb2c7 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -9712,7 +9712,7 @@ public class AudioService extends IAudioService.Stub //========================================================================================== static final int LOG_NB_EVENTS_LIFECYCLE = 20; static final int LOG_NB_EVENTS_PHONE_STATE = 20; - static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 30; + static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 50; static final int LOG_NB_EVENTS_FORCE_USE = 20; static final int LOG_NB_EVENTS_VOLUME = 40; static final int LOG_NB_EVENTS_DYN_POLICY = 10; diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 4c9b28b1bd18..d9e4828e7eb4 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.BIND_VPN_SERVICE; import static android.Manifest.permission.CONTROL_VPN; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.RouteInfo.RTN_THROW; import static android.net.RouteInfo.RTN_UNREACHABLE; import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN; @@ -2549,6 +2550,7 @@ public class Vpn { req = new NetworkRequest.Builder() .clearCapabilities() .addTransportType(NetworkCapabilities.TRANSPORT_TEST) + .addCapability(NET_CAPABILITY_NOT_VPN) .build(); } else { // Basically, the request here is referring to the default request which is defined diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index a155095c0725..982ac3c84482 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -76,6 +76,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { private final boolean mIsBootDisplayModeSupported; + private Context mOverlayContext; + // Called with SyncRoot lock held. public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener) { @@ -1222,7 +1224,10 @@ final class LocalDisplayAdapter extends DisplayAdapter { /** Supplies a context whose Resources apply runtime-overlays */ Context getOverlayContext() { - return ActivityThread.currentActivityThread().getSystemUiContext(); + if (mOverlayContext == null) { + mOverlayContext = ActivityThread.currentActivityThread().getSystemUiContext(); + } + return mOverlayContext; } /** diff --git a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java index 1c296e5b5640..8647680e52a6 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java @@ -83,7 +83,9 @@ public final class HdmiCecStandbyModeHandler { private final HdmiCecLocalDevice mDevice; private final SparseArray<CecMessageHandler> mCecMessageHandlers = new SparseArray<>(); - private final CecMessageHandler mDefaultHandler = new Aborter( + private final CecMessageHandler mDefaultHandler; + + private final CecMessageHandler mAborterUnrecognizedOpcode = new Aborter( Constants.ABORT_UNRECOGNIZED_OPCODE); private final CecMessageHandler mAborterIncorrectMode = new Aborter( Constants.ABORT_NOT_IN_CORRECT_MODE); @@ -95,6 +97,10 @@ public final class HdmiCecStandbyModeHandler { mUserControlProcessedHandler = new UserControlProcessedHandler(); private void addCommonHandlers() { + addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler); + } + + private void addTvHandlers() { addHandler(Constants.MESSAGE_ACTIVE_SOURCE, mBystander); addHandler(Constants.MESSAGE_REQUEST_ACTIVE_SOURCE, mBystander); addHandler(Constants.MESSAGE_ROUTING_CHANGE, mBystander); @@ -118,17 +124,13 @@ public final class HdmiCecStandbyModeHandler { addHandler(Constants.MESSAGE_REPORT_POWER_STATUS, mBypasser); addHandler(Constants.MESSAGE_GIVE_FEATURES, mBypasser); - addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler); - addHandler(Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS, mBypasser); addHandler(Constants.MESSAGE_ABORT, mBypasser); addHandler(Constants.MESSAGE_GET_CEC_VERSION, mBypasser); addHandler(Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, mAborterIncorrectMode); addHandler(Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE, mAborterIncorrectMode); - } - private void addTvHandlers() { addHandler(Constants.MESSAGE_IMAGE_VIEW_ON, mAutoOnHandler); addHandler(Constants.MESSAGE_TEXT_VIEW_ON, mAutoOnHandler); @@ -153,6 +155,9 @@ public final class HdmiCecStandbyModeHandler { addCommonHandlers(); if (mDevice.getType() == HdmiDeviceInfo.DEVICE_TV) { addTvHandlers(); + mDefaultHandler = mAborterUnrecognizedOpcode; + } else { + mDefaultHandler = mBypasser; } } diff --git a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java index b45bfb1c2d92..79088d0398d2 100644 --- a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java +++ b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java @@ -116,6 +116,10 @@ public class LogAccessDialogActivity extends Activity implements } mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); + if (mPackageName == null || mPackageName.length() == 0) { + throw new NullPointerException("Package Name is null"); + } + mUid = intent.getIntExtra("com.android.server.logcat.uid", 0); mGid = intent.getIntExtra("com.android.server.logcat.gid", 0); mPid = intent.getIntExtra("com.android.server.logcat.pid", 0); @@ -154,12 +158,17 @@ public class LogAccessDialogActivity extends Activity implements CharSequence appLabel = pm.getApplicationInfoAsUser(callingPackage, PackageManager.MATCH_DIRECT_BOOT_AUTO, UserHandle.getUserId(uid)).loadLabel(pm); - if (appLabel == null) { + if (appLabel == null || appLabel.length() == 0) { throw new NameNotFoundException("Application Label is null"); } - return context.getString(com.android.internal.R.string.log_access_confirmation_title, - appLabel); + String titleString = context.getString( + com.android.internal.R.string.log_access_confirmation_title, appLabel); + if (titleString == null || titleString.length() == 0) { + throw new NullPointerException("Title is null"); + } + + return titleString; } /** diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f42e734b96ec..8ed145c8d1b1 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -240,6 +240,7 @@ import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeProto; +import android.telecom.TelecomManager; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -509,6 +510,7 @@ public class NotificationManagerService extends SystemService { private ShortcutHelper mShortcutHelper; private PermissionHelper mPermissionHelper; private UsageStatsManagerInternal mUsageStatsManagerInternal; + private TelecomManager mTelecomManager; final IBinder mForegroundToken = new Binder(); private WorkerHandler mHandler; @@ -2100,7 +2102,8 @@ public class NotificationManagerService extends SystemService { NotificationHistoryManager historyManager, StatsManager statsManager, TelephonyManager telephonyManager, ActivityManagerInternal ami, MultiRateLimiter toastRateLimiter, PermissionHelper permissionHelper, - UsageStatsManagerInternal usageStatsManagerInternal) { + UsageStatsManagerInternal usageStatsManagerInternal, + TelecomManager telecomManager) { mHandler = handler; Resources resources = getContext().getResources(); mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(), @@ -2129,6 +2132,7 @@ public class NotificationManagerService extends SystemService { mDeviceIdleManager = getContext().getSystemService(DeviceIdleManager.class); mDpm = dpm; mUm = userManager; + mTelecomManager = telecomManager; mPlatformCompat = IPlatformCompat.Stub.asInterface( ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); @@ -2420,7 +2424,8 @@ public class NotificationManagerService extends SystemService { PermissionManagerServiceInternal.class), AppGlobals.getPackageManager(), AppGlobals.getPermissionManager(), mEnableAppSettingMigration, mForceUserSetOnUpgrade), - LocalServices.getService(UsageStatsManagerInternal.class)); + LocalServices.getService(UsageStatsManagerInternal.class), + getContext().getSystemService(TelecomManager.class)); publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL); @@ -5535,6 +5540,12 @@ public class NotificationManagerService extends SystemService { } @Override + public boolean isInCall(String pkg, int uid) { + checkCallerIsSystemOrSystemUiOrShell(); + return isCallNotification(pkg, uid); + } + + @Override public void setPrivateNotificationsAllowed(boolean allow) { if (PackageManager.PERMISSION_GRANTED != getContext().checkCallingPermission( @@ -6846,7 +6857,7 @@ public class NotificationManagerService extends SystemService { synchronized (mNotificationLock) { isBlocked |= isRecordBlockedLocked(r); } - if (isBlocked && !n.isMediaNotification()) { + if (isBlocked && !(n.isMediaNotification() || isCallNotification(pkg, uid, n))) { if (DBG) { Slog.e(TAG, "Suppressing notification from package " + r.getSbn().getPackageName() + " by user request."); @@ -6858,6 +6869,23 @@ public class NotificationManagerService extends SystemService { return true; } + private boolean isCallNotification(String pkg, int uid, Notification n) { + if (n.isStyle(Notification.CallStyle.class)) { + return isCallNotification(pkg, uid); + } + return false; + } + + private boolean isCallNotification(String pkg, int uid) { + final long identity = Binder.clearCallingIdentity(); + try { + return mTelecomManager.isInManagedCall() || mTelecomManager.isInSelfManagedCall( + pkg, UserHandle.getUserHandleForUid(uid)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + private boolean areNotificationsEnabledForPackageInt(String pkg, int uid) { if (mEnableAppSettingMigration) { return mPermissionHelper.hasPermission(uid); diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java index 9534ff6ae712..b4ddda551c76 100644 --- a/services/core/java/com/android/server/pm/AppsFilterImpl.java +++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java @@ -103,6 +103,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * application B is implicitly allowed to query for application A; regardless of any manifest * entries. */ + @GuardedBy("mLock") @Watched private final WatchedSparseSetArray<Integer> mImplicitlyQueryable; private final SnapshotCache<WatchedSparseSetArray<Integer>> mImplicitQueryableSnapshot; @@ -112,6 +113,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * interacted with it, but could keep across package updates. For example, if application A * grants persistable uri permission to application B; regardless of any manifest entries. */ + @GuardedBy("mLock") @Watched private final WatchedSparseSetArray<Integer> mRetainedImplicitlyQueryable; private final SnapshotCache<WatchedSparseSetArray<Integer>> @@ -121,6 +123,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * A mapping from the set of App IDs that query other App IDs via package name to the * list of packages that they can see. */ + @GuardedBy("mLock") @Watched private final WatchedSparseSetArray<Integer> mQueriesViaPackage; private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueriesViaPackageSnapshot; @@ -129,6 +132,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * A mapping from the set of App IDs that query others via component match to the list * of packages that the they resolve to. */ + @GuardedBy("mLock") @Watched private final WatchedSparseSetArray<Integer> mQueriesViaComponent; private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueriesViaComponentSnapshot; @@ -137,6 +141,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * A mapping from the set of App IDs that query other App IDs via library name to the * list of packages that they can see. */ + @GuardedBy("mLock") @Watched private final WatchedSparseSetArray<Integer> mQueryableViaUsesLibrary; private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueryableViaUsesLibrarySnapshot; @@ -159,6 +164,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * A set of App IDs that are always queryable by any package, regardless of their manifest * content. */ + @GuardedBy("mLock") @Watched private final WatchedArraySet<Integer> mForceQueryable; private final SnapshotCache<WatchedArraySet<Integer>> mForceQueryableSnapshot; @@ -176,6 +182,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable private final StateProvider mStateProvider; private SigningDetails mSystemSigningDetails; + @GuardedBy("mLock") @Watched private final WatchedArrayList<String> mProtectedBroadcasts; private final SnapshotCache<WatchedArrayList<String>> mProtectedBroadcastsSnapshot; @@ -197,6 +204,11 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable private volatile boolean mSystemReady = false; /** + * Guards the accesses for the list/set fields except for {@link #mShouldFilterCache} + */ + private final Object mLock = new Object(); + + /** * A cached snapshot. */ private final SnapshotCache<AppsFilterImpl> mSnapshot; @@ -328,20 +340,22 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * The copy constructor is used by PackageManagerService to construct a snapshot. */ private AppsFilterImpl(AppsFilterImpl orig) { - mImplicitlyQueryable = orig.mImplicitQueryableSnapshot.snapshot(); - mImplicitQueryableSnapshot = new SnapshotCache.Sealed<>(); - mRetainedImplicitlyQueryable = orig.mRetainedImplicitlyQueryableSnapshot.snapshot(); - mRetainedImplicitlyQueryableSnapshot = new SnapshotCache.Sealed<>(); - mQueriesViaPackage = orig.mQueriesViaPackageSnapshot.snapshot(); - mQueriesViaPackageSnapshot = new SnapshotCache.Sealed<>(); - mQueriesViaComponent = orig.mQueriesViaComponentSnapshot.snapshot(); - mQueriesViaComponentSnapshot = new SnapshotCache.Sealed<>(); - mQueryableViaUsesLibrary = orig.mQueryableViaUsesLibrarySnapshot.snapshot(); - mQueryableViaUsesLibrarySnapshot = new SnapshotCache.Sealed<>(); - mForceQueryable = orig.mForceQueryableSnapshot.snapshot(); - mForceQueryableSnapshot = new SnapshotCache.Sealed<>(); - mProtectedBroadcasts = orig.mProtectedBroadcastsSnapshot.snapshot(); - mProtectedBroadcastsSnapshot = new SnapshotCache.Sealed<>(); + synchronized (orig.mLock) { + mImplicitlyQueryable = orig.mImplicitQueryableSnapshot.snapshot(); + mImplicitQueryableSnapshot = new SnapshotCache.Sealed<>(); + mRetainedImplicitlyQueryable = orig.mRetainedImplicitlyQueryableSnapshot.snapshot(); + mRetainedImplicitlyQueryableSnapshot = new SnapshotCache.Sealed<>(); + mQueriesViaPackage = orig.mQueriesViaPackageSnapshot.snapshot(); + mQueriesViaPackageSnapshot = new SnapshotCache.Sealed<>(); + mQueriesViaComponent = orig.mQueriesViaComponentSnapshot.snapshot(); + mQueriesViaComponentSnapshot = new SnapshotCache.Sealed<>(); + mQueryableViaUsesLibrary = orig.mQueryableViaUsesLibrarySnapshot.snapshot(); + mQueryableViaUsesLibrarySnapshot = new SnapshotCache.Sealed<>(); + mForceQueryable = orig.mForceQueryableSnapshot.snapshot(); + mForceQueryableSnapshot = new SnapshotCache.Sealed<>(); + mProtectedBroadcasts = orig.mProtectedBroadcastsSnapshot.snapshot(); + mProtectedBroadcastsSnapshot = new SnapshotCache.Sealed<>(); + } mQueriesViaComponentRequireRecompute = orig.mQueriesViaComponentRequireRecompute; mForceQueryableByDevicePackageNames = Arrays.copyOf(orig.mForceQueryableByDevicePackageNames, @@ -742,9 +756,11 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable return false; } final boolean changed; - changed = retainOnUpdate - ? mRetainedImplicitlyQueryable.add(recipientUid, visibleUid) - : mImplicitlyQueryable.add(recipientUid, visibleUid); + synchronized (mLock) { + changed = retainOnUpdate + ? mRetainedImplicitlyQueryable.add(recipientUid, visibleUid) + : mImplicitlyQueryable.add(recipientUid, visibleUid); + } if (changed && DEBUG_LOGGING) { Slog.i(TAG, (retainOnUpdate ? "retained " : "") + "implicit access granted: " + recipientUid + " -> " + visibleUid); @@ -833,7 +849,9 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable // packages for signature matches for (PackageStateInternal setting : existingSettings.values()) { if (isSystemSigned(mSystemSigningDetails, setting)) { - mForceQueryable.add(setting.getAppId()); + synchronized (mLock) { + mForceQueryable.add(setting.getAppId()); + } } } } @@ -843,75 +861,76 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable return null; } - if (mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts())) { - mQueriesViaComponentRequireRecompute = true; - } - - final boolean newIsForceQueryable = - mForceQueryable.contains(newPkgSetting.getAppId()) - /* shared user that is already force queryable */ - || newPkgSetting.isForceQueryableOverride() /* adb override */ - || (newPkgSetting.isSystem() && (mSystemAppsQueryable - || newPkg.isForceQueryable() - || ArrayUtils.contains(mForceQueryableByDevicePackageNames, - newPkg.getPackageName()))); - if (newIsForceQueryable - || (mSystemSigningDetails != null - && isSystemSigned(mSystemSigningDetails, newPkgSetting))) { - mForceQueryable.add(newPkgSetting.getAppId()); - } + synchronized (mLock) { + if (mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts())) { + mQueriesViaComponentRequireRecompute = true; + } - for (int i = existingSettings.size() - 1; i >= 0; i--) { - final PackageStateInternal existingSetting = existingSettings.valueAt(i); - if (existingSetting.getAppId() == newPkgSetting.getAppId() - || existingSetting.getPkg() - == null) { - continue; + final boolean newIsForceQueryable = + mForceQueryable.contains(newPkgSetting.getAppId()) + /* shared user that is already force queryable */ + || newPkgSetting.isForceQueryableOverride() /* adb override */ + || (newPkgSetting.isSystem() && (mSystemAppsQueryable + || newPkg.isForceQueryable() + || ArrayUtils.contains(mForceQueryableByDevicePackageNames, + newPkg.getPackageName()))); + if (newIsForceQueryable + || (mSystemSigningDetails != null + && isSystemSigned(mSystemSigningDetails, newPkgSetting))) { + mForceQueryable.add(newPkgSetting.getAppId()); } - final AndroidPackage existingPkg = existingSetting.getPkg(); - // let's evaluate the ability of already added packages to see this new package - if (!newIsForceQueryable) { - if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(existingPkg, - newPkg, mProtectedBroadcasts)) { - mQueriesViaComponent.add(existingSetting.getAppId(), - newPkgSetting.getAppId()); - } - if (canQueryViaPackage(existingPkg, newPkg) - || canQueryAsInstaller(existingSetting, newPkg)) { - mQueriesViaPackage.add(existingSetting.getAppId(), - newPkgSetting.getAppId()); - } - if (canQueryViaUsesLibrary(existingPkg, newPkg)) { - mQueryableViaUsesLibrary.add(existingSetting.getAppId(), - newPkgSetting.getAppId()); + + for (int i = existingSettings.size() - 1; i >= 0; i--) { + final PackageStateInternal existingSetting = existingSettings.valueAt(i); + if (existingSetting.getAppId() == newPkgSetting.getAppId() + || existingSetting.getPkg() + == null) { + continue; } - } - // now we'll evaluate our new package's ability to see existing packages - if (!mForceQueryable.contains(existingSetting.getAppId())) { - if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(newPkg, - existingPkg, mProtectedBroadcasts)) { - mQueriesViaComponent.add(newPkgSetting.getAppId(), - existingSetting.getAppId()); + final AndroidPackage existingPkg = existingSetting.getPkg(); + // let's evaluate the ability of already added packages to see this new package + if (!newIsForceQueryable) { + if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(existingPkg, + newPkg, mProtectedBroadcasts)) { + mQueriesViaComponent.add(existingSetting.getAppId(), + newPkgSetting.getAppId()); + } + if (canQueryViaPackage(existingPkg, newPkg) + || canQueryAsInstaller(existingSetting, newPkg)) { + mQueriesViaPackage.add(existingSetting.getAppId(), + newPkgSetting.getAppId()); + } + if (canQueryViaUsesLibrary(existingPkg, newPkg)) { + mQueryableViaUsesLibrary.add(existingSetting.getAppId(), + newPkgSetting.getAppId()); + } } - if (canQueryViaPackage(newPkg, existingPkg) - || canQueryAsInstaller(newPkgSetting, existingPkg)) { - mQueriesViaPackage.add(newPkgSetting.getAppId(), - existingSetting.getAppId()); + // now we'll evaluate our new package's ability to see existing packages + if (!mForceQueryable.contains(existingSetting.getAppId())) { + if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(newPkg, + existingPkg, mProtectedBroadcasts)) { + mQueriesViaComponent.add(newPkgSetting.getAppId(), + existingSetting.getAppId()); + } + if (canQueryViaPackage(newPkg, existingPkg) + || canQueryAsInstaller(newPkgSetting, existingPkg)) { + mQueriesViaPackage.add(newPkgSetting.getAppId(), + existingSetting.getAppId()); + } + if (canQueryViaUsesLibrary(newPkg, existingPkg)) { + mQueryableViaUsesLibrary.add(newPkgSetting.getAppId(), + existingSetting.getAppId()); + } } - if (canQueryViaUsesLibrary(newPkg, existingPkg)) { - mQueryableViaUsesLibrary.add(newPkgSetting.getAppId(), - existingSetting.getAppId()); + // if either package instruments the other, mark both as visible to one another + if (newPkgSetting.getPkg() != null && existingSetting.getPkg() != null + && (pkgInstruments(newPkgSetting.getPkg(), existingSetting.getPkg()) + || pkgInstruments(existingSetting.getPkg(), newPkgSetting.getPkg()))) { + mQueriesViaPackage.add(newPkgSetting.getAppId(), existingSetting.getAppId()); + mQueriesViaPackage.add(existingSetting.getAppId(), newPkgSetting.getAppId()); } } - // if either package instruments the other, mark both as visible to one another - if (newPkgSetting.getPkg() != null && existingSetting.getPkg() != null - && (pkgInstruments(newPkgSetting.getPkg(), existingSetting.getPkg()) - || pkgInstruments(existingSetting.getPkg(), newPkgSetting.getPkg()))) { - mQueriesViaPackage.add(newPkgSetting.getAppId(), existingSetting.getAppId()); - mQueriesViaPackage.add(existingSetting.getAppId(), newPkgSetting.getAppId()); - } } - int existingSize = existingSettings.size(); ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize); for (int index = 0; index < existingSize; index++) { @@ -1138,16 +1157,18 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable private void collectProtectedBroadcasts( ArrayMap<String, ? extends PackageStateInternal> existingSettings, @Nullable String excludePackage) { - mProtectedBroadcasts.clear(); - for (int i = existingSettings.size() - 1; i >= 0; i--) { - PackageStateInternal setting = existingSettings.valueAt(i); - if (setting.getPkg() == null || setting.getPkg().getPackageName().equals( - excludePackage)) { - continue; - } - final List<String> protectedBroadcasts = setting.getPkg().getProtectedBroadcasts(); - if (!protectedBroadcasts.isEmpty()) { - mProtectedBroadcasts.addAll(protectedBroadcasts); + synchronized (mLock) { + mProtectedBroadcasts.clear(); + for (int i = existingSettings.size() - 1; i >= 0; i--) { + PackageStateInternal setting = existingSettings.valueAt(i); + if (setting.getPkg() == null || setting.getPkg().getPackageName().equals( + excludePackage)) { + continue; + } + final List<String> protectedBroadcasts = setting.getPkg().getProtectedBroadcasts(); + if (!protectedBroadcasts.isEmpty()) { + mProtectedBroadcasts.addAll(protectedBroadcasts); + } } } } @@ -1158,24 +1179,26 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable */ private void recomputeComponentVisibility( ArrayMap<String, ? extends PackageStateInternal> existingSettings) { - mQueriesViaComponent.clear(); - for (int i = existingSettings.size() - 1; i >= 0; i--) { - PackageStateInternal setting = existingSettings.valueAt(i); - if (setting.getPkg() == null || requestsQueryAllPackages(setting.getPkg())) { - continue; - } - for (int j = existingSettings.size() - 1; j >= 0; j--) { - if (i == j) { - continue; - } - final PackageStateInternal otherSetting = existingSettings.valueAt(j); - if (otherSetting.getPkg() == null || mForceQueryable.contains( - otherSetting.getAppId())) { + synchronized (mLock) { + mQueriesViaComponent.clear(); + for (int i = existingSettings.size() - 1; i >= 0; i--) { + PackageStateInternal setting = existingSettings.valueAt(i); + if (setting.getPkg() == null || requestsQueryAllPackages(setting.getPkg())) { continue; } - if (canQueryViaComponents(setting.getPkg(), otherSetting.getPkg(), - mProtectedBroadcasts)) { - mQueriesViaComponent.add(setting.getAppId(), otherSetting.getAppId()); + for (int j = existingSettings.size() - 1; j >= 0; j--) { + if (i == j) { + continue; + } + final PackageStateInternal otherSetting = existingSettings.valueAt(j); + if (otherSetting.getPkg() == null || mForceQueryable.contains( + otherSetting.getAppId())) { + continue; + } + if (canQueryViaComponents(setting.getPkg(), otherSetting.getPkg(), + mProtectedBroadcasts)) { + mQueriesViaComponent.add(setting.getAppId(), otherSetting.getAppId()); + } } } } @@ -1189,8 +1212,10 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable @Nullable public SparseArray<int[]> getVisibilityAllowList(PackageStateInternal setting, int[] users, ArrayMap<String, ? extends PackageStateInternal> existingSettings) { - if (mForceQueryable.contains(setting.getAppId())) { - return null; + synchronized (mLock) { + if (mForceQueryable.contains(setting.getAppId())) { + return null; + } } // let's reserve max memory to limit the number of allocations SparseArray<int[]> result = new SparseArray<>(users.length); @@ -1256,54 +1281,56 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable mStateProvider.runWithState((settings, sharedUserSettings, users) -> { final ArraySet<String> additionalChangedPackages; final int userCount = users.length; - for (int u = 0; u < userCount; u++) { - final int userId = users[u].id; - final int removingUid = UserHandle.getUid(userId, setting.getAppId()); - mImplicitlyQueryable.remove(removingUid); - for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) { - mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), - removingUid); - } + synchronized (mLock) { + for (int u = 0; u < userCount; u++) { + final int userId = users[u].id; + final int removingUid = UserHandle.getUid(userId, setting.getAppId()); + mImplicitlyQueryable.remove(removingUid); + for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) { + mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), + removingUid); + } - if (isReplace) { - continue; - } + if (isReplace) { + continue; + } - mRetainedImplicitlyQueryable.remove(removingUid); - for (int i = mRetainedImplicitlyQueryable.size() - 1; i >= 0; i--) { - mRetainedImplicitlyQueryable.remove( - mRetainedImplicitlyQueryable.keyAt(i), removingUid); + mRetainedImplicitlyQueryable.remove(removingUid); + for (int i = mRetainedImplicitlyQueryable.size() - 1; i >= 0; i--) { + mRetainedImplicitlyQueryable.remove( + mRetainedImplicitlyQueryable.keyAt(i), removingUid); + } } - } - if (!mQueriesViaComponentRequireRecompute) { - mQueriesViaComponent.remove(setting.getAppId()); - for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) { - mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), + if (!mQueriesViaComponentRequireRecompute) { + mQueriesViaComponent.remove(setting.getAppId()); + for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) { + mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), + setting.getAppId()); + } + } + mQueriesViaPackage.remove(setting.getAppId()); + for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) { + mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), + setting.getAppId()); + } + mQueryableViaUsesLibrary.remove(setting.getAppId()); + for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) { + mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i), setting.getAppId()); } - } - mQueriesViaPackage.remove(setting.getAppId()); - for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) { - mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), - setting.getAppId()); - } - mQueryableViaUsesLibrary.remove(setting.getAppId()); - for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) { - mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i), - setting.getAppId()); - } - mForceQueryable.remove(setting.getAppId()); + mForceQueryable.remove(setting.getAppId()); - if (setting.getPkg() != null - && !setting.getPkg().getProtectedBroadcasts().isEmpty()) { - final String removingPackageName = setting.getPkg().getPackageName(); - final ArrayList<String> protectedBroadcasts = new ArrayList<>(); - protectedBroadcasts.addAll(mProtectedBroadcasts.untrackedStorage()); - collectProtectedBroadcasts(settings, removingPackageName); - if (!mProtectedBroadcasts.containsAll(protectedBroadcasts)) { - mQueriesViaComponentRequireRecompute = true; + if (setting.getPkg() != null + && !setting.getPkg().getProtectedBroadcasts().isEmpty()) { + final String removingPackageName = setting.getPkg().getPackageName(); + final ArrayList<String> protectedBroadcasts = new ArrayList<>(); + protectedBroadcasts.addAll(mProtectedBroadcasts.untrackedStorage()); + collectProtectedBroadcasts(settings, removingPackageName); + if (!mProtectedBroadcasts.containsAll(protectedBroadcasts)) { + mQueriesViaComponentRequireRecompute = true; + } } } @@ -1562,11 +1589,13 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable if (DEBUG_TRACING) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mForceQueryable"); } - if (mForceQueryable.contains(targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "force queryable"); + synchronized (mLock) { + if (mForceQueryable.contains(targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "force queryable"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1577,11 +1606,13 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable if (DEBUG_TRACING) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaPackage"); } - if (mQueriesViaPackage.contains(callingAppId, targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queries package"); + synchronized (mLock) { + if (mQueriesViaPackage.contains(callingAppId, targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queries package"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1597,11 +1628,13 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable recomputeComponentVisibility(settings); }); } - if (mQueriesViaComponent.contains(callingAppId, targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queries component"); + synchronized (mLock) { + if (mQueriesViaComponent.contains(callingAppId, targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queries component"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1614,11 +1647,13 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mImplicitlyQueryable"); } final int targetUid = UserHandle.getUid(targetUserId, targetAppId); - if (mImplicitlyQueryable.contains(callingUid, targetUid)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "implicitly queryable for user"); + synchronized (mLock) { + if (mImplicitlyQueryable.contains(callingUid, targetUid)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "implicitly queryable for user"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1631,12 +1666,14 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mRetainedImplicitlyQueryable"); } final int targetUid = UserHandle.getUid(targetUserId, targetAppId); - if (mRetainedImplicitlyQueryable.contains(callingUid, targetUid)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, - "retained implicitly queryable for user"); + synchronized (mLock) { + if (mRetainedImplicitlyQueryable.contains(callingUid, targetUid)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, + "retained implicitly queryable for user"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1682,11 +1719,13 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable if (DEBUG_TRACING) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueryableViaUsesLibrary"); } - if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queryable for library users"); + synchronized (mLock) { + if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queryable for library users"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1802,23 +1841,25 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable pw.println(" system apps queryable: " + mSystemAppsQueryable); dumpPackageSet(pw, filteringAppId, mForceQueryable.untrackedStorage(), "forceQueryable", " ", expandPackages); - pw.println(" queries via package name:"); - dumpQueriesMap(pw, filteringAppId, mQueriesViaPackage, " ", expandPackages); - pw.println(" queries via component:"); - dumpQueriesMap(pw, filteringAppId, mQueriesViaComponent, " ", expandPackages); - pw.println(" queryable via interaction:"); - for (int user : users) { - pw.append(" User ").append(Integer.toString(user)).println(":"); - dumpQueriesMap(pw, - filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), - mImplicitlyQueryable, " ", expandPackages); - dumpQueriesMap(pw, - filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), - mRetainedImplicitlyQueryable, " ", expandPackages); - } - pw.println(" queryable via uses-library:"); - dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, " ", - expandPackages); + synchronized (mLock) { + pw.println(" queries via package name:"); + dumpQueriesMap(pw, filteringAppId, mQueriesViaPackage, " ", expandPackages); + pw.println(" queries via component:"); + dumpQueriesMap(pw, filteringAppId, mQueriesViaComponent, " ", expandPackages); + pw.println(" queryable via interaction:"); + for (int user : users) { + pw.append(" User ").append(Integer.toString(user)).println(":"); + dumpQueriesMap(pw, + filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), + mImplicitlyQueryable, " ", expandPackages); + dumpQueriesMap(pw, + filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), + mRetainedImplicitlyQueryable, " ", expandPackages); + } + pw.println(" queryable via uses-library:"); + dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, " ", + expandPackages); + } } private static void dumpQueriesMap(PrintWriter pw, @Nullable Integer filteringId, diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index 7dae22a44cc5..d3d291ea52ac 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -108,7 +108,7 @@ final class DeletePackageHelper { } /** - * This method is an internal method that could be get invoked either + * This method is an internal method that could be invoked either * to delete an installed package or to clean up a failed installation. * After deleting an installed package, a broadcast is sent to notify any * listeners that the package has been removed. For cleaning up a failed @@ -146,6 +146,8 @@ final class DeletePackageHelper { int[] allUsers; final int freezeUser; final SparseArray<TempUserState> priorUserStates; + + final boolean isInstallerPackage; /** enabled state of the uninstalled application */ synchronized (mPm.mLock) { final Computer computer = mPm.snapshotComputer(); @@ -226,6 +228,8 @@ final class DeletePackageHelper { freezeUser = removeUser; priorUserStates = null; } + + isInstallerPackage = mPm.mSettings.isInstallerPackage(packageName); } synchronized (mPm.mInstallLock) { @@ -324,6 +328,12 @@ final class DeletePackageHelper { } } + if (res && isInstallerPackage) { + final PackageInstallerService packageInstallerService = + mPm.mInjector.getPackageInstallerService(); + packageInstallerService.onInstallerPackageDeleted(uninstalledPs.getAppId(), removeUser); + } + return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 9b5984e09c8b..e406a1a4bca7 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -861,6 +861,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements synchronized (mSessions) { mSessions.put(sessionId, session); } + mPm.addInstallerPackageName(session.getInstallSource()); mCallbacks.notifySessionCreated(session.sessionId, session.userId); @@ -1735,4 +1736,37 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements .setPackage(sessionInfo.installerPackageName); mContext.sendBroadcastAsUser(sessionUpdatedIntent, UserHandle.of(userId)); } + + /** + * Abandon unfinished sessions if the installer package has been uninstalled. + * @param installerAppId the app ID of the installer package that has been uninstalled. + * @param userId the user that has the installer package uninstalled. + */ + void onInstallerPackageDeleted(int installerAppId, int userId) { + synchronized (mSessions) { + for (int i = 0; i < mSessions.size(); i++) { + final PackageInstallerSession session = mSessions.valueAt(i); + if (!matchesInstaller(session, installerAppId, userId)) { + continue; + } + // Find parent session and only abandon parent session if installer matches + PackageInstallerSession root = !session.hasParentSessionId() + ? session : mSessions.get(session.getParentSessionId()); + if (root != null && matchesInstaller(root, installerAppId, userId) + && !root.isDestroyed()) { + root.abandon(); + } + } + } + } + + private boolean matchesInstaller(PackageInstallerSession session, int installerAppId, + int userId) { + final int installerUid = session.getInstallerUid(); + if (installerAppId == UserHandle.USER_ALL) { + return UserHandle.getAppId(installerUid) == installerAppId; + } else { + return UserHandle.getUid(userId, installerAppId) == installerUid; + } + } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ce1ee70ca5ac..d5e2a6354494 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -7168,4 +7168,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService void notifyInstantAppPackageInstalled(String packageName, int[] newUsers) { mInstantAppRegistry.onPackageInstalled(snapshotComputer(), packageName, newUsers); } + + void addInstallerPackageName(InstallSource installSource) { + synchronized (mLock) { + mSettings.addInstallerPackageNames(installSource); + } + } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index b53cfc558f6e..e6d59d43ffbe 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -380,8 +380,8 @@ public final class Settings implements Watchable, Snappable { 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. + * List of packages that were involved in installing other packages, i.e. packages that created + * new sessions or are listed in at least one app's InstallSource. */ @Watched private final WatchedArraySet<String> mInstallerPackages; @@ -5923,4 +5923,8 @@ public final class Settings implements Watchable, Snappable { } } } + + boolean isInstallerPackage(@NonNull String packageName) { + return mInstallerPackages.contains(packageName); + } } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index ee0fdc07f841..cb08c79a7048 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -4317,16 +4317,9 @@ public class UserManagerService extends IUserManager.Stub { private long logUserCreateJourneyBegin(@UserIdInt int userId, String userType, @UserInfoFlag int flags) { - final long sessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE); - // log the journey atom with the user metadata - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId, + return logUserJourneyBegin( FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE, - /* origin_user= */ -1, userId, UserManager.getUserTypeForStatsd(userType), flags); - // log the event atom to indicate the event start - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId, - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER, - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN); - return sessionId; + userId, userType, flags); } private void logUserCreateJourneyFinish(long sessionId, @UserIdInt int userId, boolean finish) { @@ -4336,6 +4329,46 @@ public class UserManagerService extends IUserManager.Stub { : FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE); } + private long logUserRemoveJourneyBegin(@UserIdInt int userId, String userType, + @UserInfoFlag int flags) { + return logUserJourneyBegin( + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE, + userId, userType, flags); + } + + private void logUserRemoveJourneyFinish(long sessionId, @UserIdInt int userId, boolean finish) { + FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId, + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER, + finish ? FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH + : FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE); + } + + private long logUserJourneyBegin(int journey, @UserIdInt int userId, String userType, + @UserInfoFlag int flags) { + final long sessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE); + // log the journey atom with the user metadata + FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId, + journey, /* origin_user= */ -1, userId, + UserManager.getUserTypeForStatsd(userType), flags); + + // log the event atom to indicate the event start + int event; + switch (journey) { + case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE: + event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER; + break; + case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE: + event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER; + break; + default: + throw new IllegalArgumentException("Journey " + journey + " not expected."); + } + + FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId, + event, FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN); + return sessionId; + } + /** Register callbacks for statsd pulled atoms. */ private void registerStatsCallbacks() { final StatsManager statsManager = mContext.getSystemService(StatsManager.class); @@ -4578,6 +4611,10 @@ public class UserManagerService extends IUserManager.Stub { userData.info.flags |= UserInfo.FLAG_DISABLED; writeUserLP(userData); } + + final long sessionId = logUserRemoveJourneyBegin( + userId, userData.info.userType, userData.info.flags); + try { mAppOpsService.removeUser(userId); } catch (RemoteException e) { @@ -4600,9 +4637,11 @@ public class UserManagerService extends IUserManager.Stub { @Override public void userStopped(int userIdParam) { finishRemoveUser(userIdParam); + logUserRemoveJourneyFinish(sessionId, userIdParam, true); } @Override public void userStopAborted(int userIdParam) { + logUserRemoveJourneyFinish(sessionId, userIdParam, false); } }); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java index f29c40f74353..37f04501bf28 100644 --- a/services/core/java/com/android/server/vcn/Vcn.java +++ b/services/core/java/com/android/server/vcn/Vcn.java @@ -341,6 +341,9 @@ public class Vcn extends Handler { if (gatewayConnection == null) { logWtf("Found gatewayConnectionConfig without GatewayConnection"); } else { + logInfo( + "Config updated, restarting gateway " + + gatewayConnection.getLogPrefix()); gatewayConnection.teardownAsynchronously(); } } @@ -397,7 +400,7 @@ public class Vcn extends Handler { // If preexisting VcnGatewayConnection(s) satisfy request, return for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) { if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) { - logDbg("Request already satisfied by existing VcnGatewayConnection: " + request); + logVdbg("Request already satisfied by existing VcnGatewayConnection: " + request); return; } } @@ -407,8 +410,6 @@ public class Vcn extends Handler { for (VcnGatewayConnectionConfig gatewayConnectionConfig : mConfig.getGatewayConnectionConfigs()) { if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) { - logDbg("Bringing up new VcnGatewayConnection for request " + request); - if (getExposedCapabilitiesForMobileDataState(gatewayConnectionConfig).isEmpty()) { // Skip; this network does not provide any services if mobile data is disabled. continue; @@ -424,6 +425,7 @@ public class Vcn extends Handler { return; } + logInfo("Bringing up new VcnGatewayConnection for request " + request); final VcnGatewayConnection vcnGatewayConnection = mDeps.newVcnGatewayConnection( mVcnContext, @@ -455,7 +457,7 @@ public class Vcn extends Handler { } private void handleGatewayConnectionQuit(VcnGatewayConnectionConfig config) { - logDbg("VcnGatewayConnection quit: " + config); + logInfo("VcnGatewayConnection quit: " + config); mVcnGatewayConnections.remove(config); // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be satisfied @@ -534,7 +536,7 @@ public class Vcn extends Handler { // Trigger re-evaluation of all requests; mobile data state impacts supported caps. mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener); - logDbg("Mobile data " + (mIsMobileDataEnabled ? "enabled" : "disabled")); + logInfo("Mobile data " + (mIsMobileDataEnabled ? "enabled" : "disabled")); } } @@ -569,11 +571,11 @@ public class Vcn extends Handler { } private String getLogPrefix() { - return "[" + return "(" + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) + "-" + System.identityHashCode(this) - + "] "; + + ") "; } private void logVdbg(String msg) { diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index be38005abb63..cefd8efe9658 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -732,14 +732,11 @@ public class VcnGatewayConnection extends StateMachine { logDbg("Triggering async teardown"); sendDisconnectRequestedAndAcquireWakelock( DISCONNECT_REASON_TEARDOWN, true /* shouldQuit */); - - // TODO: Notify VcnInstance (via callbacks) of permanent teardown of this tunnel, since this - // is also called asynchronously when a NetworkAgent becomes unwanted } @Override protected void onQuitting() { - logDbg("Quitting VcnGatewayConnection"); + logInfo("Quitting VcnGatewayConnection"); if (mNetworkAgent != null) { logWtf("NetworkAgent was non-null in onQuitting"); @@ -794,7 +791,7 @@ public class VcnGatewayConnection extends StateMachine { // TODO(b/180132994): explore safely removing this Thread check mVcnContext.ensureRunningOnLooperThread(); - logDbg( + logInfo( "Selected underlying network changed: " + (underlying == null ? null : underlying.network)); @@ -1335,7 +1332,7 @@ public class VcnGatewayConnection extends StateMachine { protected void handleDisconnectRequested(EventDisconnectRequestedInfo info) { // TODO(b/180526152): notify VcnStatusCallback for Network loss - logDbg("Tearing down. Cause: " + info.reason); + logInfo("Tearing down. Cause: " + info.reason + "; quitting = " + info.shouldQuit); if (info.shouldQuit) { mIsQuitting.setTrue(); } @@ -1353,7 +1350,7 @@ public class VcnGatewayConnection extends StateMachine { protected void handleSafeModeTimeoutExceeded() { mSafeModeTimeoutAlarm = null; - logDbg("Entering safe mode after timeout exceeded"); + logInfo("Entering safe mode after timeout exceeded"); // Connectivity for this GatewayConnection is broken; tear down the Network. teardownNetwork(); @@ -1362,7 +1359,7 @@ public class VcnGatewayConnection extends StateMachine { } protected void logUnexpectedEvent(int what) { - logDbg( + logVdbg( "Unexpected event code " + what + " in state " @@ -1672,7 +1669,7 @@ public class VcnGatewayConnection extends StateMachine { return; } - logDbg("NetworkAgent was unwanted"); + logInfo("NetworkAgent was unwanted"); teardownAsynchronously(); } /* networkUnwantedCallback */, (status) -> { @@ -1748,7 +1745,7 @@ public class VcnGatewayConnection extends StateMachine { tunnelIface, IpSecManager.DIRECTION_FWD, transform); } } catch (IOException e) { - logDbg("Transform application failed for network " + token, e); + logInfo("Transform application failed for network " + token, e); sessionLost(token, e); } } @@ -1782,7 +1779,7 @@ public class VcnGatewayConnection extends StateMachine { tunnelIface.removeAddress(address.getAddress(), address.getPrefixLength()); } } catch (IOException e) { - logDbg("Adding address to tunnel failed for token " + token, e); + logInfo("Adding address to tunnel failed for token " + token, e); sessionLost(token, e); } } @@ -1862,7 +1859,7 @@ public class VcnGatewayConnection extends StateMachine { } private void handleMigrationCompleted(EventMigrationCompletedInfo migrationCompletedInfo) { - logDbg("Migration completed: " + mUnderlying.network); + logInfo("Migration completed: " + mUnderlying.network); applyTransform( mCurrentToken, @@ -1890,7 +1887,7 @@ public class VcnGatewayConnection extends StateMachine { mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; if (mUnderlying == null) { - logDbg("Underlying network lost"); + logInfo("Underlying network lost"); // Ignored for now; a new network may be coming up. If none does, the delayed // NETWORK_LOST disconnect will be fired, and tear down the session + network. @@ -1900,7 +1897,7 @@ public class VcnGatewayConnection extends StateMachine { // mUnderlying assumed non-null, given check above. // If network changed, migrate. Otherwise, update any existing networkAgent. if (oldUnderlying == null || !oldUnderlying.network.equals(mUnderlying.network)) { - logDbg("Migrating to new network: " + mUnderlying.network); + logInfo("Migrating to new network: " + mUnderlying.network); mIkeSession.setNetwork(mUnderlying.network); } else { // oldUnderlying is non-null & underlying network itself has not changed @@ -2168,13 +2165,13 @@ public class VcnGatewayConnection extends StateMachine { @Override public void onClosedExceptionally(@NonNull IkeException exception) { - logDbg("IkeClosedExceptionally for token " + mToken, exception); + logInfo("IkeClosedExceptionally for token " + mToken, exception); sessionClosed(mToken, exception); } @Override public void onError(@NonNull IkeProtocolException exception) { - logDbg("IkeError for token " + mToken, exception); + logInfo("IkeError for token " + mToken, exception); // Non-fatal, log and continue. } } @@ -2208,7 +2205,7 @@ public class VcnGatewayConnection extends StateMachine { @Override public void onClosedExceptionally(@NonNull IkeException exception) { - logDbg("ChildClosedExceptionally for token " + mToken, exception); + logInfo("ChildClosedExceptionally for token " + mToken, exception); sessionLost(mToken, exception); } @@ -2234,14 +2231,19 @@ public class VcnGatewayConnection extends StateMachine { } } - private String getLogPrefix() { - return "[" + // Used in Vcn.java, but must be public for mockito to mock this. + public String getLogPrefix() { + return "(" + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) + "-" + mConnectionConfig.getGatewayConnectionName() + "-" + System.identityHashCode(this) - + "] "; + + ") "; + } + + private String getTagLogPrefix() { + return "[ " + TAG + " " + getLogPrefix() + "]"; } private void logVdbg(String msg) { @@ -2258,34 +2260,44 @@ public class VcnGatewayConnection extends StateMachine { Slog.d(TAG, getLogPrefix() + msg, tr); } + private void logInfo(String msg) { + Slog.i(TAG, getLogPrefix() + msg); + LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg); + } + + private void logInfo(String msg, Throwable tr) { + Slog.i(TAG, getLogPrefix() + msg, tr); + LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg + tr); + } + private void logWarn(String msg) { Slog.w(TAG, getLogPrefix() + msg); - LOCAL_LOG.log(getLogPrefix() + "WARN: " + msg); + LOCAL_LOG.log("[WARN] " + getTagLogPrefix() + msg); } private void logWarn(String msg, Throwable tr) { Slog.w(TAG, getLogPrefix() + msg, tr); - LOCAL_LOG.log(getLogPrefix() + "WARN: " + msg + tr); + LOCAL_LOG.log("[WARN] " + getTagLogPrefix() + msg + tr); } private void logErr(String msg) { Slog.e(TAG, getLogPrefix() + msg); - LOCAL_LOG.log(getLogPrefix() + "ERR: " + msg); + LOCAL_LOG.log("[ERR ] " + getTagLogPrefix() + msg); } private void logErr(String msg, Throwable tr) { Slog.e(TAG, getLogPrefix() + msg, tr); - LOCAL_LOG.log(getLogPrefix() + "ERR: " + msg + tr); + LOCAL_LOG.log("[ERR ] " + getTagLogPrefix() + msg + tr); } private void logWtf(String msg) { Slog.wtf(TAG, getLogPrefix() + msg); - LOCAL_LOG.log(getLogPrefix() + "WTF: " + msg); + LOCAL_LOG.log("[WTF ] " + msg); } private void logWtf(String msg, Throwable tr) { Slog.wtf(TAG, getLogPrefix() + msg, tr); - LOCAL_LOG.log(getLogPrefix() + "WTF: " + msg + tr); + LOCAL_LOG.log("[WTF ] " + msg + tr); } /** diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java index ca2e449ffc25..a3babf7c9fff 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java @@ -48,6 +48,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.VcnContext; +import com.android.server.vcn.util.LogUtils; import java.util.ArrayList; import java.util.Collections; @@ -368,6 +369,18 @@ public class UnderlyingNetworkController { return; } + String allNetworkPriorities = ""; + for (UnderlyingNetworkRecord record : sorted) { + if (!allNetworkPriorities.isEmpty()) { + allNetworkPriorities += ", "; + } + allNetworkPriorities += record.network + ": " + record.getPriorityClass(); + } + logInfo( + "Selected network changed to " + + (candidate == null ? null : candidate.network) + + ", selected from list: " + + allNetworkPriorities); mCurrentRecord = candidate; mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord); } @@ -478,14 +491,38 @@ public class UnderlyingNetworkController { } } - private static void logWtf(String msg) { + private String getLogPrefix() { + return "(" + + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) + + "-" + + mConnectionConfig.getGatewayConnectionName() + + "-" + + System.identityHashCode(this) + + ") "; + } + + private String getTagLogPrefix() { + return "[ " + TAG + " " + getLogPrefix() + "]"; + } + + private void logInfo(String msg) { + Slog.i(TAG, getLogPrefix() + msg); + LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg); + } + + private void logInfo(String msg, Throwable tr) { + Slog.i(TAG, getLogPrefix() + msg, tr); + LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg + tr); + } + + private void logWtf(String msg) { Slog.wtf(TAG, msg); - LOCAL_LOG.log(TAG + " WTF: " + msg); + LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg); } - private static void logWtf(String msg, Throwable tr) { + private void logWtf(String msg, Throwable tr) { Slog.wtf(TAG, msg, tr); - LOCAL_LOG.log(TAG + " WTF: " + msg + tr); + LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg + tr); } /** Dumps the state of this record for logging and debugging purposes. */ diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java index c0488b18cb65..06f92805ad2b 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java @@ -41,11 +41,15 @@ import java.util.Objects; * @hide */ public class UnderlyingNetworkRecord { + private static final int PRIORITY_CLASS_INVALID = Integer.MAX_VALUE; + @NonNull public final Network network; @NonNull public final NetworkCapabilities networkCapabilities; @NonNull public final LinkProperties linkProperties; public final boolean isBlocked; + private int mPriorityClass = PRIORITY_CLASS_INVALID; + @VisibleForTesting(visibility = Visibility.PRIVATE) public UnderlyingNetworkRecord( @NonNull Network network, @@ -58,6 +62,34 @@ public class UnderlyingNetworkRecord { this.isBlocked = isBlocked; } + private int getOrCalculatePriorityClass( + VcnContext vcnContext, + List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + ParcelUuid subscriptionGroup, + TelephonySubscriptionSnapshot snapshot, + UnderlyingNetworkRecord currentlySelected, + PersistableBundle carrierConfig) { + // Never changes after the underlying network record is created. + if (mPriorityClass == PRIORITY_CLASS_INVALID) { + mPriorityClass = + NetworkPriorityClassifier.calculatePriorityClass( + vcnContext, + this, + underlyingNetworkTemplates, + subscriptionGroup, + snapshot, + currentlySelected, + carrierConfig); + } + + return mPriorityClass; + } + + // Used in UnderlyingNetworkController + int getPriorityClass() { + return mPriorityClass; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -84,18 +116,16 @@ public class UnderlyingNetworkRecord { PersistableBundle carrierConfig) { return (left, right) -> { final int leftIndex = - NetworkPriorityClassifier.calculatePriorityClass( + left.getOrCalculatePriorityClass( vcnContext, - left, underlyingNetworkTemplates, subscriptionGroup, snapshot, currentlySelected, carrierConfig); final int rightIndex = - NetworkPriorityClassifier.calculatePriorityClass( + right.getOrCalculatePriorityClass( vcnContext, - right, underlyingNetworkTemplates, subscriptionGroup, snapshot, @@ -142,16 +172,15 @@ public class UnderlyingNetworkRecord { pw.increaseIndent(); final int priorityIndex = - NetworkPriorityClassifier.calculatePriorityClass( + getOrCalculatePriorityClass( vcnContext, - this, underlyingNetworkTemplates, subscriptionGroup, snapshot, currentlySelected, carrierConfig); - pw.println("Priority index:" + priorityIndex); + pw.println("Priority index: " + priorityIndex); pw.println("mNetwork: " + network); pw.println("mNetworkCapabilities: " + networkCapabilities); pw.println("mLinkProperties: " + linkProperties); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index eb912d4c2747..36a7c7756a90 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2481,6 +2481,12 @@ class ActivityStarter { if (inTaskFragment == null) { inTaskFragment = TaskFragment.fromTaskFragmentToken( mOptions.getLaunchTaskFragmentToken(), mService); + if (inTaskFragment != null && inTaskFragment.isEmbeddedTaskFragmentInPip()) { + // Do not start activity in TaskFragment in a PIP Task. + Slog.w(TAG, "Can not start activity in TaskFragment in PIP: " + + inTaskFragment); + inTaskFragment = null; + } } } diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index f70dc52bf5e7..b37f980ce9a0 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -94,12 +94,22 @@ class BackNavigationController { } int backType = BackNavigationInfo.TYPE_UNDEFINED; + + // The currently visible activity (if any). + ActivityRecord currentActivity = null; + + // The currently visible task (if any). + Task currentTask = null; + + // The previous task we're going back to. Can be the same as currentTask, if there are + // multiple Activities in the Stack. Task prevTask = null; - ActivityRecord prev; + + // The previous activity we're going back to. This can be either a child of currentTask + // if there are more than one Activity in currentTask, or a child of prevTask, if + // currentActivity is the last child of currentTask. + ActivityRecord prevActivity; WindowContainer<?> removedWindowContainer = null; - ActivityRecord activityRecord = null; - ActivityRecord prevTaskTopActivity = null; - Task task = null; SurfaceControl animationLeashParent = null; HardwareBuffer screenshotBuffer = null; RemoteAnimationTarget topAppTarget = null; @@ -143,19 +153,19 @@ class BackNavigationController { } if (window == null) { - // We don't have any focused window, fallback ont the top task of the focused + // We don't have any focused window, fallback ont the top currentTask of the focused // display. ProtoLog.w(WM_DEBUG_BACK_PREVIEW, - "No focused window, defaulting to top task's window"); - task = wmService.mAtmService.getTopDisplayFocusedRootTask(); - window = task.getWindow(WindowState::isFocused); + "No focused window, defaulting to top current task's window"); + currentTask = wmService.mAtmService.getTopDisplayFocusedRootTask(); + window = currentTask.getWindow(WindowState::isFocused); } // Now let's find if this window has a callback from the client side. OnBackInvokedCallbackInfo callbackInfo = null; if (window != null) { - activityRecord = window.mActivityRecord; - task = window.getTask(); + currentActivity = window.mActivityRecord; + currentTask = window.getTask(); callbackInfo = window.getOnBackInvokedCallbackInfo(); if (callbackInfo == null) { Slog.e(TAG, "No callback registered, returning null."); @@ -167,9 +177,9 @@ class BackNavigationController { infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback()); } - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, " + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, " + "topRunningActivity=%s, callbackInfo=%s, currentFocus=%s", - task, activityRecord, callbackInfo, window); + currentTask, currentActivity, callbackInfo, window); if (window == null) { Slog.e(TAG, "Window is null, returning null."); @@ -182,18 +192,18 @@ class BackNavigationController { // - The IME is opened, and we just need to close it. // - The home activity is the focused activity. if (backType == BackNavigationInfo.TYPE_CALLBACK - || activityRecord == null - || task == null - || task.getDisplayContent().getImeContainer().isVisible() - || activityRecord.isActivityTypeHome()) { + || currentActivity == null + || currentTask == null + || currentTask.getDisplayContent().getImeContainer().isVisible() + || currentActivity.isActivityTypeHome()) { return infoBuilder .setType(backType) .build(); } // We don't have an application callback, let's find the destination of the back gesture - Task finalTask = task; - prev = task.getActivity( + Task finalTask = currentTask; + prevActivity = currentTask.getActivity( (r) -> !r.finishing && r.getTask() == finalTask && !r.isTopRunningActivity()); if (window.getParent().getChildCount() > 1 && window.getParent().getChildAt(0) != window) { @@ -201,24 +211,24 @@ class BackNavigationController { // activity, we won't close the activity. backType = BackNavigationInfo.TYPE_DIALOG_CLOSE; removedWindowContainer = window; - } else if (prev != null) { - // We have another Activity in the same task to go to + } else if (prevActivity != null) { + // We have another Activity in the same currentTask to go to backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY; - removedWindowContainer = activityRecord; - } else if (task.returnsToHomeRootTask()) { + removedWindowContainer = currentActivity; + } else if (currentTask.returnsToHomeRootTask()) { // Our Task should bring back to home - removedWindowContainer = task; + removedWindowContainer = currentTask; backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; - } else if (activityRecord.isRootOfTask()) { + } else if (currentActivity.isRootOfTask()) { // TODO(208789724): Create single source of truth for this, maybe in // RootWindowContainer - // TODO: Also check Task.shouldUpRecreateTaskLocked() for prev logic - prevTask = task.mRootWindowContainer.getTaskBelow(task); - removedWindowContainer = task; + // TODO: Also check Task.shouldUpRecreateTaskLocked() for prevActivity logic + prevTask = currentTask.mRootWindowContainer.getTaskBelow(currentTask); + removedWindowContainer = currentTask; + prevActivity = prevTask.getTopNonFinishingActivity(); if (prevTask.isActivityTypeHome()) { backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; } else { - prev = prevTask.getTopNonFinishingActivity(); backType = BackNavigationInfo.TYPE_CROSS_TASK; } } @@ -229,7 +239,7 @@ class BackNavigationController { ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Destination is Activity:%s Task:%s " + "removedContainer:%s, backType=%s", - prev != null ? prev.mActivityComponent : null, + prevActivity != null ? prevActivity.mActivityComponent : null, prevTask != null ? prevTask.getName() : null, removedWindowContainer, BackNavigationInfo.typeToString(backType)); @@ -241,7 +251,8 @@ class BackNavigationController { && !removedWindowContainer.hasCommittedReparentToAnimationLeash(); if (prepareAnimation) { - taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration; + taskWindowConfiguration = + currentTask.getTaskInfo().configuration.windowConfiguration; infoBuilder.setTaskWindowConfiguration(taskWindowConfiguration); // Prepare a leash to animate the current top window @@ -254,32 +265,36 @@ class BackNavigationController { removedWindowContainer.reparentSurfaceControl(tx, animLeash); animationLeashParent = removedWindowContainer.getAnimationLeashParent(); topAppTarget = createRemoteAnimationTargetLocked(removedWindowContainer, - activityRecord, - task, animLeash); + currentActivity, + currentTask, animLeash); infoBuilder.setDepartingAnimationTarget(topAppTarget); } //TODO(207481538) Remove once the infrastructure to support per-activity screenshot is // implemented. For now we simply have the mBackScreenshots hash map that dumbly // saves the screenshots. - if (needsScreenshot(backType) && prev != null && prev.mActivityComponent != null) { - screenshotBuffer = getActivitySnapshot(task, prev.mActivityComponent); + if (needsScreenshot(backType) && prevActivity != null + && prevActivity.mActivityComponent != null) { + screenshotBuffer = + getActivitySnapshot(currentTask, prevActivity.mActivityComponent); } - if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled()) { - task.mBackGestureStarted = true; + // Special handling for back to home animation + if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled() + && prevTask != null) { + currentTask.mBackGestureStarted = true; // Make launcher show from behind by marking its top activity as visible and // launch-behind to bump its visibility for the duration of the back gesture. - prevTaskTopActivity = prevTask.getTopNonFinishingActivity(); - if (prevTaskTopActivity != null) { - if (!prevTaskTopActivity.mVisibleRequested) { - prevTaskTopActivity.setVisibility(true); + prevActivity = prevTask.getTopNonFinishingActivity(); + if (prevActivity != null) { + if (!prevActivity.mVisibleRequested) { + prevActivity.setVisibility(true); } - prevTaskTopActivity.mLaunchTaskBehind = true; + prevActivity.mLaunchTaskBehind = true; ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Setting Activity.mLauncherTaskBehind to true. Activity=%s", - prevTaskTopActivity); - prevTaskTopActivity.mRootWindowContainer.ensureActivitiesVisible( + prevActivity); + prevActivity.mRootWindowContainer.ensureActivitiesVisible( null /* starting */, 0 /* configChanges */, false /* preserveWindows */); } @@ -290,7 +305,7 @@ class BackNavigationController { if (topAppTarget != null && needsScreenshot(backType) && prevTask != null && screenshotBuffer == null) { SurfaceControl.Builder builder = new SurfaceControl.Builder() - .setName("BackPreview Screenshot for " + prev) + .setName("BackPreview Screenshot for " + prevActivity) .setParent(animationLeashParent) .setHidden(false) .setBLASTLayer(); @@ -302,12 +317,12 @@ class BackNavigationController { // The Animation leash needs to be above the screenshot surface, but the animation leash // needs to be added before to be in the synchronized block. tx.setLayer(topAppTarget.leash, 1); - tx.apply(); - + } - WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer; + WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer; + if (finalRemovedWindowContainer != null) { try { - activityRecord.token.linkToDeath( + currentActivity.token.linkToDeath( () -> resetSurfaces(finalRemovedWindowContainer), 0); } catch (RemoteException e) { Slog.e(TAG, "Failed to link to death", e); @@ -315,11 +330,16 @@ class BackNavigationController { return null; } - RemoteCallback onBackNavigationDone = new RemoteCallback( - result -> resetSurfaces(finalRemovedWindowContainer - )); + int finalBackType = backType; + ActivityRecord finalprevActivity = prevActivity; + Task finalTask = currentTask; + RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone( + result, finalRemovedWindowContainer, finalBackType, finalTask, + finalprevActivity)); infoBuilder.setOnBackNavigationDone(onBackNavigationDone); } + + tx.apply(); return infoBuilder.build(); } @@ -348,14 +368,13 @@ class BackNavigationController { } private void onBackNavigationDone( - Bundle result, WindowContainer windowContainer, int backType, - Task task, ActivityRecord prevTaskTopActivity) { + Bundle result, WindowContainer<?> windowContainer, int backType, + Task task, ActivityRecord prevActivity) { SurfaceControl surfaceControl = windowContainer.getSurfaceControl(); - boolean triggerBack = result != null - ? result.getBoolean(BackNavigationInfo.KEY_TRIGGER_BACK) - : false; + boolean triggerBack = result != null && result.getBoolean( + BackNavigationInfo.KEY_TRIGGER_BACK); ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, " - + "task=%s, prevTaskTopActivity=%s", backType, task, prevTaskTopActivity); + + "task=%s, prevActivity=%s", backType, task, prevActivity); if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled()) { if (triggerBack) { @@ -367,13 +386,13 @@ class BackNavigationController { t.apply(); } } - if (prevTaskTopActivity != null && !triggerBack) { + if (prevActivity != null && !triggerBack) { // Restore the launch-behind state. - task.mTaskSupervisor.scheduleLaunchTaskBehindComplete(prevTaskTopActivity.token); - prevTaskTopActivity.mLaunchTaskBehind = false; + task.mTaskSupervisor.scheduleLaunchTaskBehindComplete(prevActivity.token); + prevActivity.mLaunchTaskBehind = false; ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Setting Activity.mLauncherTaskBehind to false. Activity=%s", - prevTaskTopActivity); + prevActivity); } } else { task.mBackGestureStarted = false; diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 08a9da467162..dbc08cd5d1a9 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -190,6 +190,14 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { * @see #mFullConfiguration */ public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) { + updateRequestedOverrideConfiguration(overrideConfiguration); + // Update full configuration of this container and all its children. + final ConfigurationContainer parent = getParent(); + onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY); + } + + /** Updates override configuration without recalculate full config. */ + void updateRequestedOverrideConfiguration(Configuration overrideConfiguration) { // Pre-compute this here, so we don't need to go through the entire Configuration when // writing to proto (which has significant cost if we write a lot of empty configurations). mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration); @@ -199,9 +207,6 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { && diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) { mRequestedOverrideConfiguration.windowConfiguration.setMaxBounds(newBounds); } - // Update full configuration of this container and all its children. - final ConfigurationContainer parent = getParent(); - onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY); } /** diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 566ed6076526..eaf82b625f71 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1146,8 +1146,13 @@ public class DisplayPolicy { mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win, (displayFrames, windowContainer, inOutFrame) -> { if (!mNavButtonForcedVisible) { - inOutFrame.inset(win.getLayoutingAttrs( - displayFrames.mRotation).providedInternalInsets); + final Insets[] providedInternalInsets = win.getLayoutingAttrs( + displayFrames.mRotation).providedInternalInsets; + if (providedInternalInsets != null + && providedInternalInsets.length > ITYPE_NAVIGATION_BAR + && providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) { + inOutFrame.inset(providedInternalInsets[ITYPE_NAVIGATION_BAR]); + } inOutFrame.inset(win.mGivenContentInsets); } }, @@ -1193,13 +1198,16 @@ public class DisplayPolicy { if (attrs.providesInsetsTypes != null) { for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) { final TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider = - !attrs.providedInternalImeInsets.equals(Insets.NONE) - ? (displayFrames, windowContainer, inOutFrame) -> { - inOutFrame.inset(win.getLayoutingAttrs( - displayFrames.mRotation) - .providedInternalImeInsets); - } - : null; + (displayFrames, windowContainer, inOutFrame) -> { + final Insets[] providedInternalImeInsets = + win.getLayoutingAttrs(displayFrames.mRotation) + .providedInternalImeInsets; + if (providedInternalImeInsets != null + && providedInternalImeInsets.length > insetsType + && providedInternalImeInsets[insetsType] != null) { + inOutFrame.inset(providedInternalImeInsets[insetsType]); + } + }; switch (insetsType) { case ITYPE_STATUS_BAR: mStatusBarAlt = win; @@ -1220,8 +1228,13 @@ public class DisplayPolicy { } mDisplayContent.setInsetProvider(insetsType, win, (displayFrames, windowContainer, inOutFrame) -> { - inOutFrame.inset(win.getLayoutingAttrs( - displayFrames.mRotation).providedInternalInsets); + final Insets[] providedInternalInsets = win.getLayoutingAttrs( + displayFrames.mRotation).providedInternalInsets; + if (providedInternalInsets != null + && providedInternalInsets.length > insetsType + && providedInternalInsets[insetsType] != null) { + inOutFrame.inset(providedInternalInsets[insetsType]); + } inOutFrame.inset(win.mGivenContentInsets); }, imeFrameProvider); mInsetsSourceWindowsExceptIme.add(win); @@ -1937,15 +1950,23 @@ public class DisplayPolicy { && lp.paramsForRotation[rotation] != null) { lp = lp.paramsForRotation[rotation]; } + final Insets providedInternalInsets; + if (lp.providedInternalInsets != null + && lp.providedInternalInsets.length > ITYPE_NAVIGATION_BAR + && lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) { + providedInternalInsets = lp.providedInternalInsets[ITYPE_NAVIGATION_BAR]; + } else { + providedInternalInsets = Insets.NONE; + } if (position == NAV_BAR_LEFT) { - if (lp.width > lp.providedInternalInsets.right) { - return lp.width - lp.providedInternalInsets.right; + if (lp.width > providedInternalInsets.right) { + return lp.width - providedInternalInsets.right; } else { return 0; } } else if (position == NAV_BAR_RIGHT) { - if (lp.width > lp.providedInternalInsets.left) { - return lp.width - lp.providedInternalInsets.left; + if (lp.width > providedInternalInsets.left) { + return lp.width - providedInternalInsets.left; } else { return 0; } @@ -1994,10 +2015,18 @@ public class DisplayPolicy { return 0; } LayoutParams lp = mNavigationBar.getLayoutingAttrs(rotation); - if (lp.height < lp.providedInternalInsets.top) { + final Insets providedInternalInsets; + if (lp.providedInternalInsets != null + && lp.providedInternalInsets.length > ITYPE_NAVIGATION_BAR + && lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) { + providedInternalInsets = lp.providedInternalInsets[ITYPE_NAVIGATION_BAR]; + } else { + providedInternalInsets = Insets.NONE; + } + if (lp.height < providedInternalInsets.top) { return 0; } - return lp.height - lp.providedInternalInsets.top; + return lp.height - providedInternalInsets.top; } /** diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index cc99f377bfee..d46061649676 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -24,6 +24,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; +import static android.content.res.Configuration.EMPTY; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; @@ -2005,7 +2006,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // of the activity entering PIP r.getDisplayContent().prepareAppTransition(TRANSIT_NONE); - final boolean singleActivity = task.getChildCount() == 1; + // TODO: Does it make sense to only count non-finishing activities? + final boolean singleActivity = task.getActivityCount() == 1; final Task rootTask; if (singleActivity) { rootTask = task; @@ -2086,6 +2088,15 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // TODO(task-org): Figure-out more structured way to do this long term. r.setWindowingMode(intermediateWindowingMode); r.mWaitForEnteringPinnedMode = true; + rootTask.forAllTaskFragments(tf -> { + // When the Task is entering picture-in-picture, we should clear all override from + // the client organizer, so the PIP activity can get the correct config from the + // Task, and prevent conflict with the PipTaskOrganizer. + if (tf.isOrganizedTaskFragment()) { + tf.resetAdjacentTaskFragment(); + tf.updateRequestedOverrideConfiguration(EMPTY); + } + }); rootTask.setWindowingMode(WINDOWING_MODE_PINNED); // Set the launch bounds for launch-into-pip Activity on the root task. if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index a46544d6c902..ac048434c060 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -1380,6 +1380,14 @@ class Task extends TaskFragment { return getActivity(ActivityRecord::canBeTopRunning); } + int getActivityCount() { + final int[] activityCount = new int[1]; + forAllActivities(ar -> { + activityCount[0]++; + }); + return activityCount[0]; + } + /** * Return true if any activities in this task belongs to input uid. */ diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index b96b461f2da5..83bd979a8e2f 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -339,7 +339,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { } } - private void resetAdjacentTaskFragment() { + void resetAdjacentTaskFragment() { // Reset the adjacent TaskFragment if its adjacent TaskFragment is also this TaskFragment. if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) { mAdjacentTaskFragment.mAdjacentTaskFragment = null; @@ -2317,6 +2317,14 @@ class TaskFragment extends WindowContainer<WindowContainer> { mMinHeight = minHeight; } + /** + * Whether this is an embedded TaskFragment in PIP Task. We don't allow any client config + * override for such TaskFragment to prevent flight with PipTaskOrganizer. + */ + boolean isEmbeddedTaskFragmentInPip() { + return isOrganizedTaskFragment() && getTask() != null && getTask().inPinnedWindowingMode(); + } + boolean shouldRemoveSelfOnLastChildRemoval() { return !mCreatedByOrganizer || mIsRemovalRequested; } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 5b87463d889e..4dbcea1e4751 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2761,7 +2761,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< boolean canStartChangeTransition() { return !mWmService.mDisableTransitionAnimation && mDisplayContent != null && getSurfaceControl() != null && !mDisplayContent.inTransition() - && isVisible() && isVisibleRequested() && okToAnimate(); + && isVisible() && isVisibleRequested() && okToAnimate() + // Pip animation will be handled by PipTaskOrganizer. + && !inPinnedWindowingMode() && getParent() != null + && !getParent().inPinnedWindowingMode(); } /** diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index b5cf708eb46d..c1c8b81f2c32 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -677,15 +677,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: { final IBinder fragmentToken = hop.getContainer(); - if (!mLaunchTaskFragments.containsKey(fragmentToken)) { + final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken); + if (tf == null) { final Throwable exception = new IllegalArgumentException( "Not allowed to operate with invalid fragment token"); sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); break; } + if (tf.isEmbeddedTaskFragmentInPip()) { + final Throwable exception = new IllegalArgumentException( + "Not allowed to start activity in PIP TaskFragment"); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + break; + } final Intent activityIntent = hop.getActivityIntent(); final Bundle activityOptions = hop.getLaunchOptions(); - final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken); final int result = mService.getActivityStartController() .startActivityInTaskFragment(tf, activityIntent, activityOptions, hop.getCallingActivity(), caller.mUid, caller.mPid); @@ -707,6 +713,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); break; } + if (parent.isEmbeddedTaskFragmentInPip()) { + final Throwable exception = new IllegalArgumentException( + "Not allowed to reparent activity to PIP TaskFragment"); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + break; + } if (!parent.isAllowedToEmbedActivity(activity)) { final Throwable exception = new SecurityException( "The task fragment is not trusted to embed the given activity."); @@ -730,6 +742,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); break; } + if (tf1.isEmbeddedTaskFragmentInPip() + || (tf2 != null && tf2.isEmbeddedTaskFragmentInPip())) { + final Throwable exception = new IllegalArgumentException( + "Not allowed to set adjacent on TaskFragment in PIP Task"); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + break; + } tf1.setAdjacentTaskFragment(tf2, false /* moveAdjacentTogether */); effects |= TRANSACT_EFFECTS_LIFECYCLE; @@ -1092,6 +1111,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by" + " organizer root1=" + root1 + " root2=" + root2); } + if (root1.isEmbeddedTaskFragmentInPip() || root2.isEmbeddedTaskFragmentInPip()) { + Slog.e(TAG, "Attempt to set adjacent TaskFragment in PIP Task"); + return 0; + } root1.setAdjacentTaskFragment(root2, hop.getMoveAdjacentTogether()); return TRANSACT_EFFECTS_LIFECYCLE; } @@ -1105,6 +1128,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub private int applyWindowContainerChange(WindowContainer wc, WindowContainerTransaction.Change c) { sanitizeWindowContainer(wc); + if (wc.asTaskFragment() != null && wc.asTaskFragment().isEmbeddedTaskFragmentInPip()) { + // No override from organizer for embedded TaskFragment in a PIP Task. + return 0; + } int effects = applyChanges(wc, c); @@ -1420,21 +1447,28 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return; } // The ownerActivity has to belong to the same app as the target Task. - if (ownerActivity.getTask().effectiveUid != ownerActivity.getUid() - || ownerActivity.getTask().effectiveUid != caller.mUid) { + final Task ownerTask = ownerActivity.getTask(); + if (ownerTask.effectiveUid != ownerActivity.getUid() + || ownerTask.effectiveUid != caller.mUid) { final Throwable exception = new SecurityException("Not allowed to operate with the ownerToken while " + "the root activity of the target task belong to the different app"); sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); return; } + if (ownerTask.inPinnedWindowingMode()) { + final Throwable exception = new IllegalArgumentException( + "Not allowed to create TaskFragment in PIP Task"); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + return; + } final TaskFragment taskFragment = new TaskFragment(mService, creationParams.getFragmentToken(), true /* createdByOrganizer */); // Set task fragment organizer immediately, since it might have to be notified about further // actions. taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer(), ownerActivity.getUid(), ownerActivity.info.processName); - ownerActivity.getTask().addChild(taskFragment, POSITION_TOP); + ownerTask.addChild(taskFragment, POSITION_TOP); taskFragment.setWindowingMode(creationParams.getWindowingMode()); taskFragment.setBounds(creationParams.getInitialBounds()); mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment); @@ -1467,6 +1501,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return; } } + if (newParentTF.isEmbeddedTaskFragmentInPip() || oldParent.isEmbeddedTaskFragmentInPip()) { + final Throwable exception = new SecurityException( + "Not allow to reparent in TaskFragment in PIP Task."); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + return; + } while (oldParent.hasChild()) { oldParent.getChildAt(0).reparent(newParentTF, POSITION_TOP); } @@ -1482,6 +1522,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); return 0; } + if (taskFragment.isEmbeddedTaskFragmentInPip()) { + final Throwable exception = new IllegalArgumentException( + "Not allowed to delete TaskFragment in PIP Task"); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + return 0; + } mLaunchTaskFragments.removeAt(index); taskFragment.remove(true /* withTransition */, "deleteTaskFragment"); return TRANSACT_EFFECTS_LIFECYCLE; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java index 82fe8b9362b0..6aef90c79705 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java @@ -55,8 +55,6 @@ class DeviceManagementResourcesProvider { private static final String TAG_ROOT = "root"; private static final String TAG_DRAWABLE_STYLE_ENTRY = "drawable-style-entry"; private static final String TAG_DRAWABLE_SOURCE_ENTRY = "drawable-source-entry"; - private static final String ATTR_DRAWABLE_STYLE_SIZE = "drawable-style-size"; - private static final String ATTR_DRAWABLE_SOURCE_SIZE = "drawable-source-size"; private static final String ATTR_DRAWABLE_STYLE = "drawable-style"; private static final String ATTR_DRAWABLE_SOURCE = "drawable-source"; private static final String ATTR_DRAWABLE_ID = "drawable-id"; @@ -70,9 +68,9 @@ class DeviceManagementResourcesProvider { mUpdatedDrawablesForStyle = new HashMap<>(); /** - * Map of <drawable_id, <source_id, resource_value>> + * Map of <drawable_id, <source_id, <style_id, resource_value>>> */ - private final Map<String, Map<String, ParcelableResource>> + private final Map<String, Map<String, Map<String, ParcelableResource>>> mUpdatedDrawablesForSource = new HashMap<>(); /** @@ -110,7 +108,8 @@ class DeviceManagementResourcesProvider { if (DevicePolicyResources.UNDEFINED.equals(drawableSource)) { updated |= updateDrawable(drawableId, drawableStyle, resource); } else { - updated |= updateDrawableForSource(drawableId, drawableSource, resource); + updated |= updateDrawableForSource( + drawableId, drawableSource, drawableStyle, resource); } } if (!updated) { @@ -138,19 +137,23 @@ class DeviceManagementResourcesProvider { } } - // TODO(b/214576716): change this to respect style private boolean updateDrawableForSource( - String drawableId, String drawableSource, ParcelableResource updatableResource) { + String drawableId, String drawableSource, String drawableStyle, + ParcelableResource updatableResource) { synchronized (mLock) { + Map<String, Map<String, ParcelableResource>> drawablesForId = + mUpdatedDrawablesForSource.get(drawableId); if (!mUpdatedDrawablesForSource.containsKey(drawableId)) { mUpdatedDrawablesForSource.put(drawableId, new HashMap<>()); } - ParcelableResource current = mUpdatedDrawablesForSource.get(drawableId).get( - drawableSource); + if (!drawablesForId.containsKey(drawableSource)) { + mUpdatedDrawablesForSource.get(drawableId).put(drawableSource, new HashMap<>()); + } + ParcelableResource current = drawablesForId.get(drawableSource).get(drawableStyle); if (updatableResource.equals(current)) { return false; } - mUpdatedDrawablesForSource.get(drawableId).put(drawableSource, updatableResource); + drawablesForId.get(drawableSource).put(drawableStyle, updatableResource); return true; } } @@ -175,23 +178,30 @@ class DeviceManagementResourcesProvider { } @Nullable - ParcelableResource getDrawable( - String drawableId, String drawableStyle, String drawableSource) { + ParcelableResource getDrawable(String drawableId, String drawableStyle, String drawableSource) { synchronized (mLock) { - if (mUpdatedDrawablesForSource.containsKey(drawableId) - && mUpdatedDrawablesForSource.get(drawableId).containsKey(drawableSource)) { - return mUpdatedDrawablesForSource.get(drawableId).get(drawableSource); + ParcelableResource resource = getDrawableForSourceLocked( + drawableId, drawableStyle, drawableSource); + if (resource != null) { + return resource; } if (!mUpdatedDrawablesForStyle.containsKey(drawableId)) { - Log.d(TAG, "No updated drawable found for drawable id " + drawableId); return null; } - if (mUpdatedDrawablesForStyle.get(drawableId).containsKey(drawableStyle)) { - return mUpdatedDrawablesForStyle.get(drawableId).get(drawableStyle); - } + return mUpdatedDrawablesForStyle.get(drawableId).get(drawableStyle); + } + } + + @Nullable + ParcelableResource getDrawableForSourceLocked( + String drawableId, String drawableStyle, String drawableSource) { + if (!mUpdatedDrawablesForSource.containsKey(drawableId)) { + return null; + } + if (!mUpdatedDrawablesForSource.get(drawableId).containsKey(drawableSource)) { + return null; } - Log.d(TAG, "No updated drawable found for drawable id " + drawableId); - return null; + return mUpdatedDrawablesForSource.get(drawableId).get(drawableSource).get(drawableStyle); } /** @@ -249,12 +259,8 @@ class DeviceManagementResourcesProvider { @Nullable ParcelableResource getString(String stringId) { synchronized (mLock) { - if (mUpdatedStrings.containsKey(stringId)) { - return mUpdatedStrings.get(stringId); - } + return mUpdatedStrings.get(stringId); } - Log.d(TAG, "No updated string found for string id " + stringId); - return null; } private void write() { @@ -359,50 +365,55 @@ class DeviceManagementResourcesProvider { } void writeInner(TypedXmlSerializer out) throws IOException { + writeDrawablesForStylesInner(out); + writeDrawablesForSourcesInner(out); + writeStringsInner(out); + } + + private void writeDrawablesForStylesInner(TypedXmlSerializer out) throws IOException { if (mUpdatedDrawablesForStyle != null && !mUpdatedDrawablesForStyle.isEmpty()) { for (Map.Entry<String, Map<String, ParcelableResource>> drawableEntry : mUpdatedDrawablesForStyle.entrySet()) { - out.startTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY); - out.attribute( - /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey()); - out.attributeInt( - /* namespace= */ null, - ATTR_DRAWABLE_STYLE_SIZE, - drawableEntry.getValue().size()); - int counter = 0; for (Map.Entry<String, ParcelableResource> styleEntry : drawableEntry.getValue().entrySet()) { + out.startTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY); + out.attribute( + /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey()); out.attribute( /* namespace= */ null, - ATTR_DRAWABLE_STYLE + (counter++), + ATTR_DRAWABLE_STYLE, styleEntry.getKey()); styleEntry.getValue().writeToXmlFile(out); + out.endTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY); } - out.endTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY); } } + } + + private void writeDrawablesForSourcesInner(TypedXmlSerializer out) throws IOException { if (mUpdatedDrawablesForSource != null && !mUpdatedDrawablesForSource.isEmpty()) { - for (Map.Entry<String, Map<String, ParcelableResource>> drawableEntry + for (Map.Entry<String, Map<String, Map<String, ParcelableResource>>> drawableEntry : mUpdatedDrawablesForSource.entrySet()) { - out.startTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY); - out.attribute( - /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey()); - out.attributeInt( - /* namespace= */ null, - ATTR_DRAWABLE_SOURCE_SIZE, - drawableEntry.getValue().size()); - int counter = 0; - for (Map.Entry<String, ParcelableResource> sourceEntry + for (Map.Entry<String, Map<String, ParcelableResource>> sourceEntry : drawableEntry.getValue().entrySet()) { - out.attribute( - /* namespace= */ null, - ATTR_DRAWABLE_SOURCE + (counter++), - sourceEntry.getKey()); - sourceEntry.getValue().writeToXmlFile(out); + for (Map.Entry<String, ParcelableResource> styleEntry + : sourceEntry.getValue().entrySet()) { + out.startTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY); + out.attribute(/* namespace= */ null, ATTR_DRAWABLE_ID, + drawableEntry.getKey()); + out.attribute(/* namespace= */ null, ATTR_DRAWABLE_SOURCE, + sourceEntry.getKey()); + out.attribute(/* namespace= */ null, ATTR_DRAWABLE_STYLE, + styleEntry.getKey()); + styleEntry.getValue().writeToXmlFile(out); + out.endTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY); + } } - out.endTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY); } } + } + + private void writeStringsInner(TypedXmlSerializer out) throws IOException { if (mUpdatedStrings != null && !mUpdatedStrings.isEmpty()) { for (Map.Entry<String, ParcelableResource> entry : mUpdatedStrings.entrySet()) { @@ -417,52 +428,48 @@ class DeviceManagementResourcesProvider { } } - private boolean readInner( - TypedXmlPullParser parser, int depth, String tag) + private boolean readInner(TypedXmlPullParser parser, int depth, String tag) throws XmlPullParserException, IOException { if (depth > 2) { return true; // Ignore } switch (tag) { - case TAG_DRAWABLE_STYLE_ENTRY: - String drawableId = parser.getAttributeValue( - /* namespace= */ null, ATTR_DRAWABLE_ID); - mUpdatedDrawablesForStyle.put( - drawableId, - new HashMap<>()); - int size = parser.getAttributeInt( - /* namespace= */ null, ATTR_DRAWABLE_STYLE_SIZE); - for (int i = 0; i < size; i++) { - String style = parser.getAttributeValue( - /* namespace= */ null, ATTR_DRAWABLE_STYLE + i); - mUpdatedDrawablesForStyle.get(drawableId).put( - style, - ParcelableResource.createFromXml(parser)); + case TAG_DRAWABLE_STYLE_ENTRY: { + String id = parser.getAttributeValue(/* namespace= */ null, ATTR_DRAWABLE_ID); + String style = parser.getAttributeValue( + /* namespace= */ null, ATTR_DRAWABLE_STYLE); + ParcelableResource resource = ParcelableResource.createFromXml(parser); + if (!mUpdatedDrawablesForStyle.containsKey(id)) { + mUpdatedDrawablesForStyle.put(id, new HashMap<>()); } + mUpdatedDrawablesForStyle.get(id).put(style, resource); break; - case TAG_DRAWABLE_SOURCE_ENTRY: - drawableId = parser.getAttributeValue( - /* namespace= */ null, ATTR_DRAWABLE_ID); - mUpdatedDrawablesForSource.put(drawableId, new HashMap<>()); - size = parser.getAttributeInt( - /* namespace= */ null, ATTR_DRAWABLE_SOURCE_SIZE); - for (int i = 0; i < size; i++) { - String source = parser.getAttributeValue( - /* namespace= */ null, ATTR_DRAWABLE_SOURCE + i); - mUpdatedDrawablesForSource.get(drawableId).put( - source, - ParcelableResource.createFromXml(parser)); + } + case TAG_DRAWABLE_SOURCE_ENTRY: { + String id = parser.getAttributeValue(/* namespace= */ null, ATTR_DRAWABLE_ID); + String source = parser.getAttributeValue( + /* namespace= */ null, ATTR_DRAWABLE_SOURCE); + String style = parser.getAttributeValue( + /* namespace= */ null, ATTR_DRAWABLE_STYLE); + ParcelableResource resource = ParcelableResource.createFromXml(parser); + if (!mUpdatedDrawablesForSource.containsKey(id)) { + mUpdatedDrawablesForSource.put(id, new HashMap<>()); + } + if (!mUpdatedDrawablesForSource.get(id).containsKey(source)) { + mUpdatedDrawablesForSource.get(id).put(source, new HashMap<>()); } + mUpdatedDrawablesForSource.get(id).get(source).put(style, resource); break; - case TAG_STRING_ENTRY: - String sourceId = parser.getAttributeValue( - /* namespace= */ null, ATTR_SOURCE_ID); - mUpdatedStrings.put( - sourceId, ParcelableResource.createFromXml(parser)); + } + case TAG_STRING_ENTRY: { + String id = parser.getAttributeValue(/* namespace= */ null, ATTR_SOURCE_ID); + mUpdatedStrings.put(id, ParcelableResource.createFromXml(parser)); break; - default: + } + default: { Log.e(TAG, "Unexpected tag: " + tag); return false; + } } return true; } diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index e4f1a9645e5c..db1209224bd5 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -28,6 +28,7 @@ import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareC import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.server.am.UserController.CLEAR_USER_JOURNEY_SESSION_MSG; import static com.android.server.am.UserController.COMPLETE_USER_SWITCH_MSG; import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG; import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG; @@ -410,6 +411,7 @@ public class UserControllerTest { expectedCodes.add(COMPLETE_USER_SWITCH_MSG); expectedCodes.add(REPORT_USER_SWITCH_COMPLETE_MSG); if (backgroundUserStopping) { + expectedCodes.add(CLEAR_USER_JOURNEY_SESSION_MSG); expectedCodes.add(0); // this is for directly posting in stopping. } Set<Integer> actualCodes = mInjector.mHandler.getMessageCodes(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 1cf96972c225..b987c692bddb 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -170,6 +170,7 @@ import android.service.notification.NotificationListenerService; import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; import android.service.notification.ZenPolicy; +import android.telecom.TelecomManager; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -290,6 +291,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Mock ActivityManager mActivityManager; @Mock + TelecomManager mTelecomManager; + @Mock Resources mResources; @Mock RankingHandler mRankingHandler; @@ -494,7 +497,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, mAppOpsManager, mAppOpsService, mUm, mHistoryManager, mStatsManager, mock(TelephonyManager.class), - mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class)); + mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class), + mTelecomManager); // Return first true for RoleObserver main-thread check when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper); @@ -9127,6 +9131,54 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testCallNotificationsBypassBlock() throws Exception { + when(mAmi.getPendingIntentFlags(any(IIntentSender.class))) + .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT); + when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); + + Notification.Builder nb = new Notification.Builder( + mContext, mTestNotificationChannel.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .addAction(new Notification.Action.Builder(null, "test", null).build()); + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, + nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); + NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mBinderService.setNotificationsEnabledForPackage( + r.getSbn().getPackageName(), r.getUid(), false); + + // normal blocked notifications - blocked + assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), + r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse(); + + // just using the style - blocked + Person person = new Person.Builder() + .setName("caller") + .build(); + nb.setStyle(Notification.CallStyle.forOngoingCall( + person, mock(PendingIntent.class))); + nb.setFullScreenIntent(mock(PendingIntent.class), true); + sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, + nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); + r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), + r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse(); + + // style + managed call - bypasses block + when(mTelecomManager.isInManagedCall()).thenReturn(true); + assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), + r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue(); + + // style + self managed call - bypasses block + when(mTelecomManager.isInSelfManagedCall( + r.getSbn().getPackageName(), r.getUser())).thenReturn(true); + assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), + r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue(); + } + + @Test public void testGetAllUsersNotificationPermissions_migrationNotEnabled() { // make sure we don't bother if the migration is not enabled assertThat(mService.getAllUsersNotificationPermissions()).isNull(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java index 0f6d5a56c667..b751c7fc73ea 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java @@ -97,6 +97,7 @@ import android.os.UserManager; import android.provider.Settings; import android.service.notification.NotificationListenerFilter; import android.service.notification.StatusBarNotification; +import android.telecom.TelecomManager; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -379,7 +380,7 @@ public class NotificationPermissionMigrationTest extends UiServiceTestCase { mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, mAppOpsManager, mock(IAppOpsService.class), mUm, mHistoryManager, mStatsManager, mock(TelephonyManager.class), mAmi, mToastRateLimiter, mPermissionHelper, - mock(UsageStatsManagerInternal.class)); + mock(UsageStatsManagerInternal.class), mock(TelecomManager.class)); // Return first true for RoleObserver main-thread check when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java index 1d25b54207c4..0bfd2020622f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java @@ -52,6 +52,7 @@ import android.content.pm.PackageManagerInternal; import android.os.Looper; import android.os.UserHandle; import android.os.UserManager; +import android.telecom.TelecomManager; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -167,7 +168,7 @@ public class RoleObserverTest extends UiServiceTestCase { mock(StatsManager.class), mock(TelephonyManager.class), mock(ActivityManagerInternal.class), mock(MultiRateLimiter.class), mock(PermissionHelper.class), - mock(UsageStatsManagerInternal.class)); + mock(UsageStatsManagerInternal.class), mock (TelecomManager.class)); } catch (SecurityException e) { if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { throw e; diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index fe59185a6893..49cd343ef4af 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -29,9 +29,12 @@ 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.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.NonNull; @@ -39,6 +42,7 @@ import android.annotation.Nullable; import android.hardware.HardwareBuffer; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; import android.view.WindowManager; import android.window.BackEvent; import android.window.BackNavigationInfo; @@ -77,15 +81,22 @@ public class BackNavigationControllerTests extends WindowTestsBase { @Test public void backNavInfo_HomeWhenBackToLauncher() { - IOnBackInvokedCallback callback = withSystemCallback(createTopTaskWithActivity()); + Task task = createTopTaskWithActivity(); + IOnBackInvokedCallback callback = withSystemCallback(task); - BackNavigationInfo backNavigationInfo = startBackNavigation(); + SurfaceControl.Transaction tx = mock(SurfaceControl.Transaction.class); + BackNavigationInfo backNavigationInfo = mBackNavigationController.startBackNavigation(mWm, + tx); assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull(); assertThat(backNavigationInfo.getDepartingAnimationTarget()).isNotNull(); assertThat(backNavigationInfo.getTaskWindowConfiguration()).isNotNull(); assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME)); + + verify(tx, atLeastOnce()).apply(); + verify(tx, times(1)).reparent(any(), + eq(backNavigationInfo.getDepartingAnimationTarget().leash)); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 762c08f99d94..8ef9ada8995f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -300,6 +300,81 @@ public class RootWindowContainerTests extends WindowTestsBase { assertTrue(firstActivity.mRequestForceTransition); } + /** + * When there is only one activity in the Task, and the activity is requesting to enter PIP, the + * whole Task should enter PIP. + */ + @Test + public void testSingleActivityTaskEnterPip() { + final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(fullscreenTask) + .build(); + final Task task = activity.getTask(); + + // Move activity to pinned root task. + mRootWindowContainer.moveActivityToPinnedRootTask(activity, + null /* launchIntoPipHostActivity */, "test"); + + // Ensure a task has moved over. + ensureTaskPlacement(task, activity); + assertTrue(task.inPinnedWindowingMode()); + } + + /** + * When there is only one activity in the Task, and the activity is requesting to enter PIP, the + * whole Task should enter PIP even if the activity is in a TaskFragment. + */ + @Test + public void testSingleActivityInTaskFragmentEnterPip() { + final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setParentTask(fullscreenTask) + .createActivityCount(1) + .build(); + final ActivityRecord activity = taskFragment.getTopMostActivity(); + final Task task = activity.getTask(); + + // Move activity to pinned root task. + mRootWindowContainer.moveActivityToPinnedRootTask(activity, + null /* launchIntoPipHostActivity */, "test"); + + // Ensure a task has moved over. + ensureTaskPlacement(task, activity); + assertTrue(task.inPinnedWindowingMode()); + } + + /** + * When there is one TaskFragment with two activities in the Task, the activity requests to + * enter PIP, that activity will be move to PIP root task. + */ + @Test + public void testMultipleActivitiesInTaskFragmentEnterPip() { + final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setParentTask(fullscreenTask) + .createActivityCount(2) + .build(); + final ActivityRecord firstActivity = taskFragment.getTopMostActivity(); + final ActivityRecord secondActivity = taskFragment.getBottomMostActivity(); + + // Move first activity to pinned root task. + mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, + null /* launchIntoPipHostActivity */, "test"); + + final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea(); + final Task pinnedRootTask = taskDisplayArea.getRootPinnedTask(); + + // Ensure a task has moved over. + ensureTaskPlacement(pinnedRootTask, firstActivity); + ensureTaskPlacement(fullscreenTask, secondActivity); + assertTrue(pinnedRootTask.inPinnedWindowingMode()); + assertEquals(WINDOWING_MODE_FULLSCREEN, fullscreenTask.getWindowingMode()); + } + private static void ensureTaskPlacement(Task task, ActivityRecord... activities) { final ArrayList<ActivityRecord> taskActivities = new ArrayList<>(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index 37719fecd7e4..d135de0fc921 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -16,6 +16,9 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -28,6 +31,8 @@ import static org.junit.Assert.assertNull; 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.clearInvocations; import static org.mockito.Mockito.mock; @@ -435,6 +440,107 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { } @Test + public void testTaskFragmentInPip_startActivityInTaskFragment() { + setupTaskFragmentInPip(); + final ActivityRecord activity = mTaskFragment.getTopMostActivity(); + final IBinder errorToken = new Binder(); + spyOn(mAtm.getActivityStartController()); + spyOn(mAtm.mWindowOrganizerController); + + // Not allow to start activity in a TaskFragment that is in a PIP Task. + mTransaction.startActivityInTaskFragment( + mFragmentToken, activity.token, new Intent(), null /* activityOptions */) + .setErrorCallbackToken(errorToken); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + verify(mAtm.getActivityStartController(), never()).startActivityInTaskFragment(any(), any(), + any(), any(), anyInt(), anyInt()); + verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(errorToken), any(IllegalArgumentException.class)); + } + + @Test + public void testTaskFragmentInPip_reparentActivityToTaskFragment() { + setupTaskFragmentInPip(); + final ActivityRecord activity = createActivityRecord(mDisplayContent); + final IBinder errorToken = new Binder(); + spyOn(mAtm.mWindowOrganizerController); + + // Not allow to reparent activity to a TaskFragment that is in a PIP Task. + mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token) + .setErrorCallbackToken(errorToken); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(errorToken), any(IllegalArgumentException.class)); + assertNull(activity.getOrganizedTaskFragment()); + } + + @Test + public void testTaskFragmentInPip_setAdjacentTaskFragment() { + setupTaskFragmentInPip(); + final IBinder errorToken = new Binder(); + spyOn(mAtm.mWindowOrganizerController); + + // Not allow to set adjacent on a TaskFragment that is in a PIP Task. + mTransaction.setAdjacentTaskFragments(mFragmentToken, null /* fragmentToken2 */, + null /* options */) + .setErrorCallbackToken(errorToken); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(errorToken), any(IllegalArgumentException.class)); + verify(mTaskFragment, never()).setAdjacentTaskFragment(any(), anyBoolean()); + } + + @Test + public void testTaskFragmentInPip_createTaskFragment() { + mController.registerOrganizer(mIOrganizer); + final Task pipTask = createTask(mDisplayContent, WINDOWING_MODE_PINNED, + ACTIVITY_TYPE_STANDARD); + final ActivityRecord activity = createActivityRecord(pipTask); + final IBinder fragmentToken = new Binder(); + final IBinder errorToken = new Binder(); + spyOn(mAtm.mWindowOrganizerController); + + // Not allow to create TaskFragment in a PIP Task. + createTaskFragmentFromOrganizer(mTransaction, activity, fragmentToken); + mTransaction.setErrorCallbackToken(errorToken); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(errorToken), any(IllegalArgumentException.class)); + assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken)); + } + + @Test + public void testTaskFragmentInPip_deleteTaskFragment() { + setupTaskFragmentInPip(); + final IBinder errorToken = new Binder(); + spyOn(mAtm.mWindowOrganizerController); + + // Not allow to delete a TaskFragment that is in a PIP Task. + mTransaction.deleteTaskFragment(mFragmentWindowToken) + .setErrorCallbackToken(errorToken); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(errorToken), any(IllegalArgumentException.class)); + assertNotNull(mAtm.mWindowOrganizerController.getTaskFragment(mFragmentToken)); + } + + @Test + public void testTaskFragmentInPip_setConfig() { + setupTaskFragmentInPip(); + spyOn(mAtm.mWindowOrganizerController); + + // Set bounds is ignored on a TaskFragment that is in a PIP Task. + mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100)); + + verify(mTaskFragment, never()).setBounds(any()); + } + + @Test public void testDeferPendingTaskFragmentEventsOfInvisibleTask() { // Task - TaskFragment - Activity. final Task task = createTask(mDisplayContent); @@ -643,4 +749,20 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { fail(); } } + + /** Setups an embedded TaskFragment in a PIP Task. */ + private void setupTaskFragmentInPip() { + mOrganizer.applyTransaction(mTransaction); + mController.registerOrganizer(mIOrganizer); + mTaskFragment = new TaskFragmentBuilder(mAtm) + .setCreateParentTask() + .setFragmentToken(mFragmentToken) + .setOrganizer(mOrganizer) + .createActivityCount(1) + .build(); + mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken(); + mAtm.mWindowOrganizerController.mLaunchTaskFragments + .put(mFragmentToken, mTaskFragment); + mTaskFragment.getTask().setWindowingMode(WINDOWING_MODE_PINNED); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java index 2101b6e767c8..30c2413eb6f7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; @@ -31,6 +32,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.clearInvocations; +import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; import android.platform.test.annotations.Presubmit; @@ -207,6 +209,39 @@ public class TaskFragmentTest extends WindowTestsBase { } @Test + public void testEmbeddedTaskFragmentEnterPip_resetOrganizerOverrideConfig() { + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setOrganizer(mOrganizer) + .setFragmentToken(new Binder()) + .setCreateParentTask() + .createActivityCount(1) + .build(); + final Task task = taskFragment.getTask(); + final ActivityRecord activity = taskFragment.getTopMostActivity(); + final Rect taskFragmentBounds = new Rect(0, 0, 300, 1000); + task.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + taskFragment.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + taskFragment.setBounds(taskFragmentBounds); + + assertEquals(taskFragmentBounds, activity.getBounds()); + assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode()); + + // Move activity to pinned root task. + mRootWindowContainer.moveActivityToPinnedRootTask(activity, + null /* launchIntoPipHostActivity */, "test"); + + // Ensure taskFragment requested config is reset. + assertEquals(taskFragment, activity.getOrganizedTaskFragment()); + assertEquals(task, activity.getTask()); + assertTrue(task.inPinnedWindowingMode()); + assertTrue(taskFragment.inPinnedWindowingMode()); + final Rect taskBounds = task.getBounds(); + assertEquals(taskBounds, taskFragment.getBounds()); + assertEquals(taskBounds, activity.getBounds()); + assertEquals(Configuration.EMPTY, taskFragment.getRequestedOverrideConfiguration()); + } + + @Test public void testActivityHasOverlayOverUntrustedModeEmbedded() { final Task rootTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index b2cc104834e6..1b07e9a69446 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -237,7 +237,12 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser @Override public void onUEvent(UEventObserver.UEvent event) { if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString()); - sEventLogger.log(new UsbDeviceLogger.StringEvent("USB UEVENT: " + event.toString())); + if (sEventLogger != null) { + sEventLogger.log(new UsbDeviceLogger.StringEvent("USB UEVENT: " + + event.toString())); + } else { + if (DEBUG) Slog.d(TAG, "sEventLogger == null"); + } String state = event.get("USB_STATE"); String accessory = event.get("ACCESSORY"); |