diff options
62 files changed, 858 insertions, 364 deletions
diff --git a/apex/permission/service/java/com/android/permission/persistence/IoUtils.java b/apex/permission/service/java/com/android/permission/persistence/IoUtils.java index 0ae44603516e..569a78c0ab41 100644 --- a/apex/permission/service/java/com/android/permission/persistence/IoUtils.java +++ b/apex/permission/service/java/com/android/permission/persistence/IoUtils.java @@ -20,6 +20,8 @@ import android.annotation.NonNull; /** * Utility class for IO. + * + * @hide */ public class IoUtils { diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index ed0ea556dc9d..ac00a042b79e 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -520,6 +520,12 @@ public abstract class AccessibilityService extends Service { */ public static final int GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT = 13; + /** + * Action to show Launcher's all apps. + * @hide + */ + public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14; + private static final String LOG_TAG = "AccessibilityService"; /** diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index 8d472da1fb7c..570cc2c11738 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -116,22 +116,25 @@ public class BiometricManager { /** * Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the - * requirements for <strong>Strong</strong>, as defined by the Android CDD. + * requirements for <strong>Tier 3</strong> (formerly <strong>Strong</strong>), as defined + * by the Android CDD. */ int BIOMETRIC_STRONG = 0x000F; /** * Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the - * requirements for <strong>Weak</strong>, as defined by the Android CDD. + * requirements for <strong>Tier 2</strong> (formerly <strong>Weak</strong>), as defined by + * the Android CDD. * * <p>Note that this is a superset of {@link #BIOMETRIC_STRONG} and is defined such that - * <code>BIOMETRIC_STRONG | BIOMETRIC_WEAK == BIOMETRIC_WEAK</code>. + * {@code BIOMETRIC_STRONG | BIOMETRIC_WEAK == BIOMETRIC_WEAK}. */ int BIOMETRIC_WEAK = 0x00FF; /** * Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the - * requirements for <strong>Convenience</strong>, as defined by the Android CDD. + * requirements for <strong>Tier 1</strong> (formerly <strong>Convenience</strong>), as + * defined by the Android CDD. * * <p>This constant is intended for use by {@link android.provider.DeviceConfig} to adjust * the reported strength of a biometric sensor. It is not a valid parameter for any of the diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java index b387a68dd8a3..68943bf2a83a 100644 --- a/core/java/android/view/autofill/AutofillId.java +++ b/core/java/android/view/autofill/AutofillId.java @@ -75,7 +75,10 @@ public final class AutofillId implements Parcelable { /** @hide */ public static AutofillId withoutSession(@NonNull AutofillId id) { final int flags = id.mFlags & ~FLAG_HAS_SESSION; - return new AutofillId(flags, id.mViewId, id.mVirtualLongId, NO_SESSION); + final long virtualChildId = + ((id.mFlags & FLAG_IS_VIRTUAL_LONG) != 0) ? id.mVirtualLongId + : id.mVirtualIntId; + return new AutofillId(flags, id.mViewId, virtualChildId, NO_SESSION); } /** @hide */ diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java index 493865ac563f..b723db287823 100644 --- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java @@ -151,6 +151,13 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { mOnProfileSelectedListener.onProfileSelected(position); } } + + @Override + public void onPageScrollStateChanged(int state) { + if (mOnProfileSelectedListener != null) { + mOnProfileSelectedListener.onProfilePageStateChanged(state); + } + } }); viewPager.setAdapter(this); viewPager.setCurrentItem(mCurrentPage); @@ -606,6 +613,17 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { * {@link #PROFILE_WORK} if the work profile was selected. */ void onProfileSelected(int profileIndex); + + + /** + * Callback for when the scroll state changes. Useful for discovering when the user begins + * dragging, when the pager is automatically settling to the current page, or when it is + * fully stopped/idle. + * @param state {@link ViewPager#SCROLL_STATE_IDLE}, {@link ViewPager#SCROLL_STATE_DRAGGING} + * or {@link ViewPager#SCROLL_STATE_SETTLING} + * @see ViewPager.OnPageChangeListener#onPageScrollStateChanged + */ + void onProfilePageStateChanged(int state); } /** diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 2a43287a3ae3..ff34c86fea0a 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -102,6 +102,7 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewTreeObserver; +import android.view.WindowInsets; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.Button; @@ -129,6 +130,7 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.GridLayoutManager; import com.android.internal.widget.RecyclerView; import com.android.internal.widget.ResolverDrawerLayout; +import com.android.internal.widget.ViewPager; import com.google.android.collect.Lists; @@ -204,6 +206,10 @@ public class ChooserActivity extends ResolverActivity implements public static final int SELECTION_TYPE_STANDARD = 3; public static final int SELECTION_TYPE_COPY = 4; + private static final int SCROLL_STATUS_IDLE = 0; + private static final int SCROLL_STATUS_SCROLLING_VERTICAL = 1; + private static final int SCROLL_STATUS_SCROLLING_HORIZONTAL = 2; + // statsd logger wrapper protected ChooserActivityLogger mChooserActivityLogger; @@ -293,6 +299,7 @@ public class ChooserActivity extends ResolverActivity implements protected MetricsLogger mMetricsLogger; private ContentPreviewCoordinator mPreviewCoord; + private int mScrollStatus = SCROLL_STATUS_IDLE; @VisibleForTesting protected ChooserMultiProfilePagerAdapter mChooserMultiProfilePagerAdapter; @@ -2680,7 +2687,7 @@ public class ChooserActivity extends ResolverActivity implements offset = Math.min(offset, minHeight); } } else { - ViewGroup currentEmptyStateView = getCurrentEmptyStateView(); + ViewGroup currentEmptyStateView = getActiveEmptyStateView(); if (currentEmptyStateView.getVisibility() == View.VISIBLE) { offset += currentEmptyStateView.getHeight(); } @@ -2705,7 +2712,7 @@ public class ChooserActivity extends ResolverActivity implements return -1; } - private ViewGroup getCurrentEmptyStateView() { + private ViewGroup getActiveEmptyStateView() { int currentPage = mChooserMultiProfilePagerAdapter.getCurrentPage(); return mChooserMultiProfilePagerAdapter.getItem(currentPage).getEmptyStateView(); } @@ -2822,10 +2829,20 @@ public class ChooserActivity extends ResolverActivity implements final float defaultElevation = elevatedView.getElevation(); final float chooserHeaderScrollElevation = getResources().getDimensionPixelSize(R.dimen.chooser_header_scroll_elevation); - mChooserMultiProfilePagerAdapter.getActiveAdapterView().addOnScrollListener( new RecyclerView.OnScrollListener() { public void onScrollStateChanged(RecyclerView view, int scrollState) { + if (scrollState == RecyclerView.SCROLL_STATE_IDLE) { + if (mScrollStatus == SCROLL_STATUS_SCROLLING_VERTICAL) { + mScrollStatus = SCROLL_STATUS_IDLE; + setHorizontalScrollingEnabled(true); + } + } else if (scrollState == RecyclerView.SCROLL_STATE_DRAGGING) { + if (mScrollStatus == SCROLL_STATUS_IDLE) { + mScrollStatus = SCROLL_STATUS_SCROLLING_VERTICAL; + setHorizontalScrollingEnabled(false); + } + } } public void onScrolled(RecyclerView view, int dx, int dy) { @@ -3030,6 +3047,44 @@ public class ChooserActivity extends ResolverActivity implements intent.fixUris(UserHandle.myUserId()); } + @Override + protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { + if (shouldShowTabs()) { + mChooserMultiProfilePagerAdapter + .setEmptyStateBottomOffset(insets.getSystemWindowInsetBottom()); + mChooserMultiProfilePagerAdapter.setupContainerPadding( + getActiveEmptyStateView().findViewById(R.id.resolver_empty_state_container)); + } + return super.onApplyWindowInsets(v, insets); + } + + private void setHorizontalScrollingEnabled(boolean enabled) { + ResolverViewPager viewPager = findViewById(R.id.profile_pager); + viewPager.setSwipingEnabled(enabled); + } + + private void setVerticalScrollEnabled(boolean enabled) { + ChooserGridLayoutManager layoutManager = + (ChooserGridLayoutManager) mChooserMultiProfilePagerAdapter.getActiveAdapterView() + .getLayoutManager(); + layoutManager.setVerticalScrollEnabled(enabled); + } + + @Override + void onHorizontalSwipeStateChanged(int state) { + if (state == ViewPager.SCROLL_STATE_DRAGGING) { + if (mScrollStatus == SCROLL_STATUS_IDLE) { + mScrollStatus = SCROLL_STATUS_SCROLLING_HORIZONTAL; + setVerticalScrollEnabled(false); + } + } else if (state == ViewPager.SCROLL_STATE_IDLE) { + if (mScrollStatus == SCROLL_STATUS_SCROLLING_VERTICAL) { + mScrollStatus = SCROLL_STATUS_IDLE; + setVerticalScrollEnabled(true); + } + } + } + /** * Adapter for all types of items and targets in ShareSheet. * Note that ranked sections like Direct Share - while appearing grid-like - are handled on the diff --git a/core/java/com/android/internal/app/ChooserGridLayoutManager.java b/core/java/com/android/internal/app/ChooserGridLayoutManager.java index 317a987cf359..c50ebd9562c9 100644 --- a/core/java/com/android/internal/app/ChooserGridLayoutManager.java +++ b/core/java/com/android/internal/app/ChooserGridLayoutManager.java @@ -28,6 +28,8 @@ import com.android.internal.widget.RecyclerView; */ public class ChooserGridLayoutManager extends GridLayoutManager { + private boolean mVerticalScrollEnabled = true; + /** * Constructor used when layout manager is set in XML by RecyclerView attribute * "layoutManager". If spanCount is not specified in the XML, it defaults to a @@ -67,4 +69,13 @@ public class ChooserGridLayoutManager extends GridLayoutManager { // Do not count the footer view in the official count return super.getRowCountForAccessibility(recycler, state) - 1; } + + void setVerticalScrollEnabled(boolean verticalScrollEnabled) { + mVerticalScrollEnabled = verticalScrollEnabled; + } + + @Override + public boolean canScrollVertically() { + return mVerticalScrollEnabled && super.canScrollVertically(); + } } diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java index 774be3c9c4b8..ffa6041721c6 100644 --- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java @@ -38,6 +38,7 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd private final ChooserProfileDescriptor[] mItems; private final boolean mIsSendAction; + private int mBottomOffset; ChooserMultiProfilePagerAdapter(Context context, ChooserActivity.ChooserGridAdapter adapter, @@ -245,6 +246,16 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd } } + void setEmptyStateBottomOffset(int bottomOffset) { + mBottomOffset = bottomOffset; + } + + @Override + protected void setupContainerPadding(View container) { + container.setPadding(container.getPaddingLeft(), container.getPaddingTop(), + container.getPaddingRight(), container.getPaddingBottom() + mBottomOffset); + } + class ChooserProfileDescriptor extends ProfileDescriptor { private ChooserActivity.ChooserGridAdapter chooserGridAdapter; private RecyclerView recyclerView; diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index f96f560fc60f..838d9bc6fa43 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -1653,10 +1653,18 @@ public class ResolverActivity extends Activity implements viewPager.setVisibility(View.VISIBLE); tabHost.setCurrentTab(mMultiProfilePagerAdapter.getCurrentPage()); mMultiProfilePagerAdapter.setOnProfileSelectedListener( - index -> { - tabHost.setCurrentTab(index); - resetButtonBar(); - resetCheckedItem(); + new AbstractMultiProfilePagerAdapter.OnProfileSelectedListener() { + @Override + public void onProfileSelected(int index) { + tabHost.setCurrentTab(index); + resetButtonBar(); + resetCheckedItem(); + } + + @Override + public void onProfilePageStateChanged(int state) { + onHorizontalSwipeStateChanged(state); + } }); mMultiProfilePagerAdapter.setOnSwitchOnWorkSelectedListener( () -> { @@ -1668,6 +1676,8 @@ public class ResolverActivity extends Activity implements findViewById(R.id.resolver_tab_divider).setVisibility(View.VISIBLE); } + void onHorizontalSwipeStateChanged(int state) {} + private void maybeHideDivider() { if (!isIntentPicker()) { return; diff --git a/core/java/com/android/internal/app/ResolverViewPager.java b/core/java/com/android/internal/app/ResolverViewPager.java index 4eb6e3bd2071..9cdfc2f5c763 100644 --- a/core/java/com/android/internal/app/ResolverViewPager.java +++ b/core/java/com/android/internal/app/ResolverViewPager.java @@ -18,6 +18,7 @@ package com.android.internal.app; import android.content.Context; import android.util.AttributeSet; +import android.view.MotionEvent; import android.view.View; import com.android.internal.widget.ViewPager; @@ -30,6 +31,8 @@ import com.android.internal.widget.ViewPager; */ public class ResolverViewPager extends ViewPager { + private boolean mSwipingEnabled = true; + public ResolverViewPager(Context context) { super(context); } @@ -70,4 +73,13 @@ public class ResolverViewPager extends ViewPager { heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } + + void setSwipingEnabled(boolean swipingEnabled) { + mSwipingEnabled = swipingEnabled; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return mSwipingEnabled && super.onInterceptTouchEvent(ev); + } } diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index fc2005a31696..3d8cae8e74d0 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -526,8 +526,16 @@ static void UnsetChldSignalHandler() { // Calls POSIX setgroups() using the int[] object as an argument. // A nullptr argument is tolerated. -static void SetGids(JNIEnv* env, jintArray managed_gids, fail_fn_t fail_fn) { +static void SetGids(JNIEnv* env, jintArray managed_gids, jboolean is_child_zygote, + fail_fn_t fail_fn) { if (managed_gids == nullptr) { + if (is_child_zygote) { + // For child zygotes like webview and app zygote, we want to clear out + // any supplemental groups the parent zygote had. + if (setgroups(0, NULL) == -1) { + fail_fn(CREATE_ERROR("Failed to remove supplementary groups for child zygote")); + } + } return; } @@ -1665,7 +1673,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, } } - SetGids(env, gids, fail_fn); + SetGids(env, gids, is_child_zygote, fail_fn); SetRLimits(env, rlimits, fail_fn); if (need_pre_initialize_native_bridge) { diff --git a/core/proto/android/stats/mediametrics/mediametrics.proto b/core/proto/android/stats/mediametrics/mediametrics.proto index e1af9622adb3..9f0ff591a506 100644 --- a/core/proto/android/stats/mediametrics/mediametrics.proto +++ b/core/proto/android/stats/mediametrics/mediametrics.proto @@ -131,7 +131,7 @@ message AudioTrackData { * Logged from: * frameworks/av/media/libstagefright/MediaCodec.cpp * frameworks/av/services/mediaanalytics/statsd_codec.cpp - * Next Tag: 21 + * Next Tag: 26 */ message CodecData { optional string codec = 1; @@ -156,6 +156,9 @@ message CodecData { optional int64 latency_unknown = 20; optional int32 queue_input_buffer_error = 21; optional int32 queue_secure_input_buffer_error = 22; + optional string bitrate_mode = 23; + optional int32 bitrate = 24; + optional int64 lifetime_millis = 25; } /** diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index dc21e878d132..a1c2450be153 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1613,15 +1613,15 @@ <string name="face_error_no_space">Can\u2019t store new face data. Delete an old one first.</string> <!-- Generic error message shown when the face operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user. [CHAR LIMIT=50] --> <string name="face_error_canceled">Face operation canceled.</string> - <!-- Generic error message shown when the face unlock operation is canceled due to user input. Generally not shown to the user [CHAR LIMIT=54] --> + <!-- Generic error message shown when the face unlock operation is canceled due to user input. Generally not shown to the user [CHAR LIMIT=68] --> <string name="face_error_user_canceled">Face unlock canceled by user.</string> <!-- Generic error message shown when the face operation fails because too many attempts have been made. [CHAR LIMIT=50] --> <string name="face_error_lockout">Too many attempts. Try again later.</string> - <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=71] --> + <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=77] --> <string name="face_error_lockout_permanent">Too many attempts. Face unlock disabled.</string> <!-- Generic error message shown when the face hardware can't recognize the face. [CHAR LIMIT=50] --> <string name="face_error_unable_to_process">Can\u2019t verify face. Try again.</string> - <!-- Generic error message shown when the user has no enrolled face. [CHAR LIMIT=52] --> + <!-- Generic error message shown when the user has no enrolled face. [CHAR LIMIT=59] --> <string name="face_error_not_enrolled">You haven\u2019t set up face unlock.</string> <!-- Generic error message shown when the app requests face unlock on a device without a sensor. [CHAR LIMIT=61] --> <string name="face_error_hw_not_present">Face unlock is not supported on this device.</string> diff --git a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java index a8ca6f048a11..b329e55b569f 100644 --- a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java +++ b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java @@ -126,6 +126,32 @@ public class AutofillIdTest { } @Test + public void testVirtual_Long_withoutSession() { + final AutofillId id = new AutofillId(new AutofillId(42), 108L, 666); + final AutofillId idWithoutSession = AutofillId.withoutSession(id); + assertThat(idWithoutSession.getViewId()).isEqualTo(42); + assertThat(idWithoutSession.isVirtualLong()).isTrue(); + assertThat(idWithoutSession.isVirtualInt()).isFalse(); + assertThat(idWithoutSession.isNonVirtual()).isFalse(); + assertThat(idWithoutSession.getVirtualChildLongId()).isEqualTo(108L); + assertThat(idWithoutSession.getVirtualChildIntId()).isEqualTo(View.NO_ID); + assertThat(idWithoutSession.getSessionId()).isEqualTo(NO_SESSION); + } + + @Test + public void testVirtual_Int_withoutSession() { + final AutofillId id = new AutofillId(42, 108); + final AutofillId idWithoutSession = AutofillId.withoutSession(id); + assertThat(idWithoutSession.getViewId()).isEqualTo(42); + assertThat(idWithoutSession.isVirtualLong()).isFalse(); + assertThat(idWithoutSession.isVirtualInt()).isTrue(); + assertThat(idWithoutSession.isNonVirtual()).isFalse(); + assertThat(idWithoutSession.getVirtualChildIntId()).isEqualTo(108); + assertThat(idWithoutSession.getVirtualChildLongId()).isEqualTo(View.NO_ID); + assertThat(idWithoutSession.getSessionId()).isEqualTo(NO_SESSION); + } + + @Test public void testSetResetSession() { final AutofillId id = new AutofillId(42); assertNonVirtual(id, 42, NO_SESSION); diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp index 2a8a39a1fe1a..32b33a758535 100644 --- a/packages/CarSystemUI/Android.bp +++ b/packages/CarSystemUI/Android.bp @@ -32,6 +32,7 @@ android_library { "SystemUIPluginLib", "SystemUISharedLib", "SettingsLib", + "car-ui-lib", "android.car.userlib", "androidx.legacy_legacy-support-v4", "androidx.recyclerview_recyclerview", @@ -95,6 +96,7 @@ android_library { "androidx.slice_slice-builders", "androidx.arch.core_core-runtime", "androidx.lifecycle_lifecycle-extensions", + "car-ui-lib", "SystemUI-tags", "SystemUI-proto", "metrics-helper-lib", diff --git a/packages/CarSystemUI/res/drawable/nav_button_background.xml b/packages/CarSystemUI/res/drawable/nav_button_background.xml deleted file mode 100644 index 376347cdf4a9..000000000000 --- a/packages/CarSystemUI/res/drawable/nav_button_background.xml +++ /dev/null @@ -1,26 +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 - --> - -<ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:color="@color/nav_bar_ripple_background_color"> - <item android:id="@android:id/mask"> - <shape android:shape="rectangle"> - <solid android:color="?android:colorAccent"/> - <corners android:radius="6dp"/> - </shape> - </item> -</ripple> diff --git a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml index a8c70989253e..94816f81a4c5 100644 --- a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml @@ -79,7 +79,7 @@ android:gravity="bottom" android:orientation="vertical"> - <com.android.keyguard.AlphaOptimizedImageButton + <com.android.systemui.statusbar.AlphaOptimizedImageView android:id="@+id/note" android:layout_height="wrap_content" android:layout_width="match_parent" diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml index 2a715d0c3494..93174983b116 100644 --- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml @@ -29,9 +29,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" - android:paddingStart="20dp" + android:gravity="center" + android:layoutDirection="ltr" android:paddingEnd="20dp" - android:gravity="center"> + android:paddingStart="20dp"> <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/home" @@ -135,9 +136,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" - android:paddingStart="@dimen/car_keyline_1" - android:paddingEnd="@dimen/car_keyline_1" android:gravity="center" + android:layoutDirection="ltr" + android:paddingEnd="@dimen/car_keyline_1" + android:paddingStart="@dimen/car_keyline_1" android:visibility="gone" /> diff --git a/packages/CarSystemUI/res/layout/car_navigation_button.xml b/packages/CarSystemUI/res/layout/car_navigation_button.xml index ca4e76ee104b..a8f115742023 100644 --- a/packages/CarSystemUI/res/layout/car_navigation_button.xml +++ b/packages/CarSystemUI/res/layout/car_navigation_button.xml @@ -27,7 +27,7 @@ android:animateLayoutChanges="true" android:orientation="vertical"> - <com.android.keyguard.AlphaOptimizedImageButton + <com.android.systemui.statusbar.AlphaOptimizedImageView android:id="@+id/car_nav_button_icon_image" android:layout_height="@dimen/car_navigation_button_icon_height" android:layout_width="match_parent" @@ -40,7 +40,7 @@ android:clickable="false" /> - <com.android.keyguard.AlphaOptimizedImageButton + <com.android.systemui.statusbar.AlphaOptimizedImageView android:id="@+id/car_nav_button_more_icon" android:layout_height="wrap_content" android:layout_width="match_parent" diff --git a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml index fd75570e759c..dc9583382921 100644 --- a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml @@ -82,7 +82,7 @@ android:gravity="bottom" android:orientation="vertical"> - <com.android.keyguard.AlphaOptimizedImageButton + <com.android.systemui.statusbar.AlphaOptimizedImageView android:id="@+id/note" android:layout_height="wrap_content" android:layout_width="match_parent" diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml index 60e0d7e430df..cdc29eec21cd 100644 --- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml @@ -27,7 +27,8 @@ <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_weight="1"> + android:layout_weight="1" + android:layoutDirection="ltr"> <FrameLayout android:id="@+id/left_hvac_container" diff --git a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml index caf1677234d0..1782d2536035 100644 --- a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml +++ b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml @@ -29,6 +29,15 @@ android:orientation="horizontal" app:layout_constraintGuide_begin="@dimen/headsup_scrim_height"/> + <!-- Include a FocusParkingView at the beginning or end. The rotary controller "parks" the + focus here when the user navigates to another window. This is also used to prevent + wrap-around which is why it must be first or last in Tab order. --> + <com.android.car.ui.FocusParkingView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toTopOf="parent"/> + <View android:id="@+id/scrim" android:layout_width="match_parent" diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml index 371bebdebc86..7fc69e6d5d8f 100644 --- a/packages/CarSystemUI/res/values/styles.xml +++ b/packages/CarSystemUI/res/values/styles.xml @@ -44,6 +44,6 @@ <style name="NavigationBarButton"> <item name="android:layout_height">96dp</item> <item name="android:layout_width">96dp</item> - <item name="android:background">@drawable/nav_button_background</item> + <item name="android:background">@*android:drawable/item_background_material</item> </style> </resources>
\ No newline at end of file diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java index ab61b443df97..2dad5f872e73 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java @@ -219,6 +219,14 @@ public class CarKeyguardViewController extends OverlayViewController implements } @Override + public void setOccluded(boolean occluded, boolean animate) { + getOverlayViewGlobalStateController().setOccluded(occluded); + if (!occluded) { + reset(/* hideBouncerWhenShowing= */ false); + } + } + + @Override public void onCancelClicked() { if (mBouncer == null) return; @@ -315,11 +323,6 @@ public class CarKeyguardViewController extends OverlayViewController implements } @Override - public void setOccluded(boolean occluded, boolean animate) { - // no-op - } - - @Override public boolean shouldDisableWindowAnimationsForUnlock() { return false; } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java index 20fc1bcd6013..0ced4021ce38 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java @@ -74,8 +74,10 @@ public class CarNavigationBarView extends LinearLayout { mDarkIconManager.setShouldLog(true); Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager); } - // needs to be clickable so that it will receive ACTION_MOVE events + // Needs to be clickable so that it will receive ACTION_MOVE events. setClickable(true); + // Needs to not be focusable so rotary won't highlight the entire nav bar. + setFocusable(false); } @Override diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java index 5e113d6366a1..e7e33a5439f9 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java @@ -32,8 +32,8 @@ import android.widget.ImageView; import android.widget.LinearLayout; import com.android.internal.annotations.VisibleForTesting; -import com.android.keyguard.AlphaOptimizedImageButton; import com.android.systemui.R; +import com.android.systemui.statusbar.AlphaOptimizedImageView; import java.net.URISyntaxException; @@ -53,8 +53,8 @@ public class CarNavigationButton extends LinearLayout { private static final String EXTRA_BUTTON_PACKAGES = "packages"; private Context mContext; - private AlphaOptimizedImageButton mIcon; - private AlphaOptimizedImageButton mMoreIcon; + private AlphaOptimizedImageView mIcon; + private AlphaOptimizedImageView mMoreIcon; private ImageView mUnseenIcon; private String mIntent; private String mLongIntent; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java index 3b7b48a77186..d60bc418ece2 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java @@ -24,6 +24,7 @@ import android.view.ViewGroup; import androidx.annotation.LayoutRes; +import com.android.car.ui.FocusParkingView; import com.android.systemui.R; import javax.inject.Inject; @@ -146,6 +147,12 @@ public class NavigationBarViewFactory { CarNavigationBarView view = (CarNavigationBarView) View.inflate(mContext, barLayout, /* root= */ null); + + // Include a FocusParkingView at the end. The rotary controller "parks" the focus here when + // the user navigates to another window. This is also used to prevent wrap-around which is + // why it must be first or last in Tab order. + view.addView(new FocusParkingView(mContext)); + mCachedViewMap.put(type, view); return mCachedViewMap.get(type); } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java index aeb1d39599db..d4f720715a69 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java @@ -24,7 +24,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; -import android.widget.FrameLayout; import com.android.car.notification.R; import com.android.car.notification.headsup.CarHeadsUpNotificationContainer; @@ -44,7 +43,7 @@ public class CarHeadsUpNotificationSystemContainer implements CarHeadsUpNotifica private final OverlayViewGlobalStateController mOverlayViewGlobalStateController; private final ViewGroup mWindow; - private final FrameLayout mHeadsUpContentFrame; + private final ViewGroup mHeadsUpContentFrame; @Inject CarHeadsUpNotificationSystemContainer(Context context, diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java index 30e26578bd73..3969f92c690a 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java @@ -138,4 +138,11 @@ public class OverlayViewController { protected boolean shouldShowNavigationBar() { return false; } + + /** + * Returns {@code true} if this view should be hidden during the occluded state. + */ + protected boolean shouldShowWhenOccluded() { + return false; + } } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java index 70260b0d4cef..8e9410964313 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java @@ -24,7 +24,9 @@ import androidx.annotation.VisibleForTesting; import com.android.systemui.car.navigationbar.CarNavigationBarController; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; @@ -47,11 +49,16 @@ public class OverlayViewGlobalStateController { private static final int UNKNOWN_Z_ORDER = -1; private final SystemUIOverlayWindowController mSystemUIOverlayWindowController; private final CarNavigationBarController mCarNavigationBarController; + + private boolean mIsOccluded; + @VisibleForTesting Map<OverlayViewController, Integer> mZOrderMap; @VisibleForTesting SortedMap<Integer, OverlayViewController> mZOrderVisibleSortedMap; @VisibleForTesting + Set<OverlayViewController> mViewsHiddenForOcclusion; + @VisibleForTesting OverlayViewController mHighestZOrder; @Inject @@ -63,6 +70,7 @@ public class OverlayViewGlobalStateController { mCarNavigationBarController = carNavigationBarController; mZOrderMap = new HashMap<>(); mZOrderVisibleSortedMap = new TreeMap<>(); + mViewsHiddenForOcclusion = new HashSet<>(); } /** @@ -91,6 +99,10 @@ public class OverlayViewGlobalStateController { */ public void showView(OverlayViewController viewController, @Nullable Runnable show) { debugLog(); + if (mIsOccluded && !viewController.shouldShowWhenOccluded()) { + mViewsHiddenForOcclusion.add(viewController); + return; + } if (mZOrderVisibleSortedMap.isEmpty()) { setWindowVisible(true); } @@ -147,6 +159,10 @@ public class OverlayViewGlobalStateController { */ public void hideView(OverlayViewController viewController, @Nullable Runnable hide) { debugLog(); + if (mIsOccluded && mViewsHiddenForOcclusion.contains(viewController)) { + mViewsHiddenForOcclusion.remove(viewController); + return; + } if (!viewController.isInflated()) { Log.d(TAG, "Content cannot be hidden since it isn't inflated: " + viewController.getClass().getName()); @@ -240,6 +256,43 @@ public class OverlayViewGlobalStateController { return mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowHUN(); } + /** + * Set the OverlayViewWindow to be in occluded or unoccluded state. When OverlayViewWindow is + * occluded, all views mounted to it that are not configured to be shown during occlusion will + * be hidden. + */ + public void setOccluded(boolean occluded) { + if (occluded) { + // Hide views before setting mIsOccluded to true so the regular hideView logic is used, + // not the one used during occlusion. + hideViewsForOcclusion(); + mIsOccluded = true; + } else { + mIsOccluded = false; + // show views after setting mIsOccluded to false so the regular showView logic is used, + // not the one used during occlusion. + showViewsHiddenForOcclusion(); + } + } + + private void hideViewsForOcclusion() { + HashSet<OverlayViewController> viewsCurrentlyShowing = new HashSet<>( + mZOrderVisibleSortedMap.values()); + viewsCurrentlyShowing.forEach(overlayController -> { + if (!overlayController.shouldShowWhenOccluded()) { + hideView(overlayController, overlayController::hideInternal); + mViewsHiddenForOcclusion.add(overlayController); + } + }); + } + + private void showViewsHiddenForOcclusion() { + mViewsHiddenForOcclusion.forEach(overlayViewController -> { + showView(overlayViewController, overlayViewController::showInternal); + }); + mViewsHiddenForOcclusion.clear(); + } + private void debugLog() { if (!DEBUG) { return; @@ -250,5 +303,8 @@ public class OverlayViewGlobalStateController { Log.d(TAG, "mZOrderVisibleSortedMap: " + mZOrderVisibleSortedMap); Log.d(TAG, "mZOrderMap.size(): " + mZOrderMap.size()); Log.d(TAG, "mZOrderMap: " + mZOrderMap); + Log.d(TAG, "mIsOccluded: " + mIsOccluded); + Log.d(TAG, "mViewsHiddenForOcclusion: " + mViewsHiddenForOcclusion); + Log.d(TAG, "mViewsHiddenForOcclusion.size(): " + mViewsHiddenForOcclusion.size()); } } diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java index 38836d85e8d4..189e240169c3 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java @@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -169,6 +170,18 @@ public class CarKeyguardViewControllerTest extends SysuiTestCase { } @Test + public void setOccludedFalse_currentlyOccluded_bouncerReset() { + when(mBouncer.isSecure()).thenReturn(true); + mCarKeyguardViewController.show(/* options= */ null); + mCarKeyguardViewController.setOccluded(/* occluded= */ true, /* animate= */ false); + reset(mBouncer); + + mCarKeyguardViewController.setOccluded(/* occluded= */ false, /* animate= */ false); + + verify(mBouncer).show(/* resetSecuritySelection= */ true); + } + + @Test public void onCancelClicked_callsCancelClickedListener() { when(mBouncer.isSecure()).thenReturn(true); mCarKeyguardViewController.show(/* options= */ null); diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java index 54282d39998b..bcaa5e9a03ee 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java @@ -36,8 +36,8 @@ import android.widget.LinearLayout; import androidx.test.filters.SmallTest; -import com.android.keyguard.AlphaOptimizedImageButton; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.AlphaOptimizedImageView; import com.android.systemui.tests.R; import org.junit.Before; @@ -74,7 +74,7 @@ public class CarNavigationButtonTest extends SysuiTestCase { @Test public void onCreate_iconIsVisible() { - AlphaOptimizedImageButton icon = mDefaultButton.findViewById( + AlphaOptimizedImageView icon = mDefaultButton.findViewById( R.id.car_nav_button_icon_image); assertThat(icon.getDrawable()).isNotNull(); @@ -83,12 +83,12 @@ public class CarNavigationButtonTest extends SysuiTestCase { @Test public void onSelected_selectedIconDefined_togglesIcon() { mDefaultButton.setSelected(true); - Drawable selectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById( + Drawable selectedIconDrawable = ((AlphaOptimizedImageView) mDefaultButton.findViewById( R.id.car_nav_button_icon_image)).getDrawable(); mDefaultButton.setSelected(false); - Drawable unselectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById( + Drawable unselectedIconDrawable = ((AlphaOptimizedImageView) mDefaultButton.findViewById( R.id.car_nav_button_icon_image)).getDrawable(); assertThat(selectedIconDrawable).isNotEqualTo(unselectedIconDrawable); @@ -100,12 +100,12 @@ public class CarNavigationButtonTest extends SysuiTestCase { R.id.selected_icon_undefined); selectedIconUndefinedButton.setSelected(true); - Drawable selectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById( + Drawable selectedIconDrawable = ((AlphaOptimizedImageView) mDefaultButton.findViewById( R.id.car_nav_button_icon_image)).getDrawable(); selectedIconUndefinedButton.setSelected(false); - Drawable unselectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById( + Drawable unselectedIconDrawable = ((AlphaOptimizedImageView) mDefaultButton.findViewById( R.id.car_nav_button_icon_image)).getDrawable(); assertThat(selectedIconDrawable).isEqualTo(unselectedIconDrawable); @@ -150,7 +150,7 @@ public class CarNavigationButtonTest extends SysuiTestCase { @Test public void onSelected_doesNotShowMoreWhenSelected_doesNotShowMoreIcon() { mDefaultButton.setSelected(true); - AlphaOptimizedImageButton moreIcon = mDefaultButton.findViewById( + AlphaOptimizedImageView moreIcon = mDefaultButton.findViewById( R.id.car_nav_button_more_icon); assertThat(moreIcon.getVisibility()).isEqualTo(View.GONE); @@ -161,7 +161,7 @@ public class CarNavigationButtonTest extends SysuiTestCase { CarNavigationButton showMoreWhenSelected = mTestView.findViewById( R.id.not_highlightable_more_button); showMoreWhenSelected.setSelected(true); - AlphaOptimizedImageButton moreIcon = showMoreWhenSelected.findViewById( + AlphaOptimizedImageView moreIcon = showMoreWhenSelected.findViewById( R.id.car_nav_button_more_icon); assertThat(moreIcon.getVisibility()).isEqualTo(View.VISIBLE); @@ -173,7 +173,7 @@ public class CarNavigationButtonTest extends SysuiTestCase { R.id.highlightable_no_more_button); showMoreWhenSelected.setSelected(true); showMoreWhenSelected.setSelected(false); - AlphaOptimizedImageButton moreIcon = showMoreWhenSelected.findViewById( + AlphaOptimizedImageView moreIcon = showMoreWhenSelected.findViewById( R.id.car_nav_button_more_icon); assertThat(moreIcon.getVisibility()).isEqualTo(View.GONE); @@ -187,7 +187,7 @@ public class CarNavigationButtonTest extends SysuiTestCase { roleBasedButton.setSelected(false); roleBasedButton.setAppIcon(appIcon); - Drawable currentDrawable = ((AlphaOptimizedImageButton) roleBasedButton.findViewById( + Drawable currentDrawable = ((AlphaOptimizedImageView) roleBasedButton.findViewById( R.id.car_nav_button_icon_image)).getDrawable(); assertThat(currentDrawable).isEqualTo(appIcon); @@ -212,7 +212,7 @@ public class CarNavigationButtonTest extends SysuiTestCase { roleBasedButton.setSelected(true); roleBasedButton.setAppIcon(appIcon); - Drawable currentDrawable = ((AlphaOptimizedImageButton) roleBasedButton.findViewById( + Drawable currentDrawable = ((AlphaOptimizedImageView) roleBasedButton.findViewById( R.id.car_nav_button_icon_image)).getDrawable(); assertThat(currentDrawable).isEqualTo(appIcon); diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java index 9e6e616e3ccf..cba42e5a9be4 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java @@ -491,6 +491,81 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { } @Test + public void setOccludedTrue_viewToHideWhenOccludedVisible_viewHidden() { + setupOverlayViewController1(); + setOverlayViewControllerAsShowing(mOverlayViewController1); + when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(false); + + mOverlayViewGlobalStateController.setOccluded(true); + + assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue( + mOverlayViewController1)).isFalse(); + } + + @Test + public void setOccludedTrue_viewToNotHideWhenOccludedVisible_viewShown() { + setupOverlayViewController1(); + setOverlayViewControllerAsShowing(mOverlayViewController1); + when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(true); + + mOverlayViewGlobalStateController.setOccluded(true); + + assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue( + mOverlayViewController1)).isTrue(); + } + + @Test + public void hideViewAndThenSetOccludedTrue_viewHiddenForOcclusion_viewHiddenAfterOcclusion() { + setupOverlayViewController1(); + setOverlayViewControllerAsShowing(mOverlayViewController1); + when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(false); + mOverlayViewGlobalStateController.setOccluded(true); + + mOverlayViewGlobalStateController.hideView(mOverlayViewController1, /* runnable= */ null); + mOverlayViewGlobalStateController.setOccluded(false); + + assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue( + mOverlayViewController1)).isFalse(); + } + + @Test + public void setOccludedTrueAndThenShowView_viewToNotHideForOcclusion_viewShown() { + setupOverlayViewController1(); + when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(true); + + mOverlayViewGlobalStateController.setOccluded(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); + + assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue( + mOverlayViewController1)).isTrue(); + } + + @Test + public void setOccludedTrueAndThenShowView_viewToHideForOcclusion_viewHidden() { + setupOverlayViewController1(); + when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(false); + + mOverlayViewGlobalStateController.setOccluded(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); + + assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue( + mOverlayViewController1)).isFalse(); + } + + @Test + public void setOccludedFalse_viewShownAfterSetOccludedTrue_viewToHideForOcclusion_viewShown() { + setupOverlayViewController1(); + when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(false); + mOverlayViewGlobalStateController.setOccluded(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); + + mOverlayViewGlobalStateController.setOccluded(false); + + assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue( + mOverlayViewController1)).isTrue(); + } + + @Test public void inflateView_notInflated_inflates() { when(mOverlayViewController2.isInflated()).thenReturn(false); diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index d039c9f646df..1b5062efa23e 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -140,8 +140,8 @@ <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Адкрытая сетка"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Бяспечная сетка"</string> <string name="process_kernel_label" msgid="950292573930336765">"АС Android"</string> - <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Выдаленыя прыкладанні"</string> - <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Выдаленыя прыкладанні і карыстальнiкi"</string> + <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Выдаленыя праграмы"</string> + <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Выдаленыя праграмы і карыстальнiкi"</string> <string name="data_usage_ota" msgid="7984667793701597001">"Абнаўленні сістэмы"</string> <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB-мадэм"</string> <string name="tether_settings_title_wifi" msgid="4803402057533895526">"Партатыўны хот-спот"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 65f456e20e4e..d003ef0b9c71 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -449,7 +449,7 @@ <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täislaadimiseni"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Tundmatu"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Laadimine"</string> - <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Kiiresti laadimine"</string> + <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Kiirlaadimine"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Aeglaselt laadimine"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ei lae"</string> <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Vooluvõrgus, praegu ei saa laadida"</string> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt new file mode 100644 index 000000000000..8cd68ef8acbc --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt @@ -0,0 +1,26 @@ +package com.android.keyguard + +import android.annotation.CurrentTimeMillisLong + +/** + * Data class for tracking information associated with [KeyguardUpdateMonitor.shouldListenForFace] + * method calls. + */ +data class KeyguardFaceListenModel( + @CurrentTimeMillisLong val timeMillis: Long, + val userId: Int, + val isListeningForFace: Boolean, + val isBouncer: Boolean, + val isAuthInterruptActive: Boolean, + val isKeyguardAwake: Boolean, + val isListeningForFaceAssistant: Boolean, + val isSwitchingUser: Boolean, + val isFaceDisabled: Boolean, + val isBecauseCannotSkipBouncer: Boolean, + val isKeyguardGoingAway: Boolean, + val isFaceSettingEnabledForUser: Boolean, + val isLockIconPressed: Boolean, + val isScanningAllowedByStrongAuth: Boolean, + val isPrimaryUser: Boolean, + val isSecureCameraLaunched: Boolean +) diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 0db713e9c276..ee31706c0b94 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -58,6 +58,7 @@ import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; +import android.os.Build; import android.os.CancellationSignal; import android.os.Handler; import android.os.IRemoteCallback; @@ -108,9 +109,13 @@ import com.google.android.collect.Lists; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; +import java.text.SimpleDateFormat; +import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.TimeZone; @@ -131,7 +136,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private static final String TAG = "KeyguardUpdateMonitor"; private static final boolean DEBUG = KeyguardConstants.DEBUG; private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES; - private static final boolean DEBUG_FACE = true; + private static final boolean DEBUG_FACE = Build.IS_DEBUGGABLE; private static final boolean DEBUG_SPEW = false; private static final int LOW_BATTERY_THRESHOLD = 20; @@ -362,6 +367,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @VisibleForTesting SparseArray<BiometricAuthenticated> mUserFaceAuthenticated = new SparseArray<>(); + // Keep track of recent calls to shouldListenForFace() for debugging. + private static final int FACE_LISTEN_CALLS_QUEUE_SIZE = 20; + private ArrayDeque<KeyguardFaceListenModel> mFaceListenModels; + private static int sCurrentUser; private Runnable mUpdateBiometricListeningState = this::updateBiometricListeningState; @@ -1945,25 +1954,48 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab && strongAuthAllowsScanning && mIsPrimaryUser && !mSecureCameraLaunched; + // Aggregate relevant fields for debug logging. + if (DEBUG_FACE || DEBUG_SPEW) { + final KeyguardFaceListenModel model = new KeyguardFaceListenModel( + System.currentTimeMillis(), + user, + shouldListen, + mBouncer, + mAuthInterruptActive, + awakeKeyguard, + shouldListenForFaceAssistant(), + mSwitchingUser, + isFaceDisabled(user), + becauseCannotSkipBouncer, + mKeyguardGoingAway, + mFaceSettingEnabledForUser.get(user), + mLockIconPressed, + strongAuthAllowsScanning, + mIsPrimaryUser, + mSecureCameraLaunched); + maybeLogFaceListenerModelData(model); + } + + return shouldListen; + } + + private void maybeLogFaceListenerModelData(KeyguardFaceListenModel model) { // Too chatty, but very useful when debugging issues. if (DEBUG_SPEW) { - Log.v(TAG, "shouldListenForFace(" + user + ")=" + shouldListen + "... " - + ", mBouncer: " + mBouncer - + ", mAuthInterruptActive: " + mAuthInterruptActive - + ", awakeKeyguard: " + awakeKeyguard - + ", shouldListenForFaceAssistant: " + shouldListenForFaceAssistant() - + ", mSwitchingUser: " + mSwitchingUser - + ", isFaceDisabled(" + user + "): " + isFaceDisabled(user) - + ", becauseCannotSkipBouncer: " + becauseCannotSkipBouncer - + ", mKeyguardGoingAway: " + mKeyguardGoingAway - + ", mFaceSettingEnabledForUser(" + user + "): " - + mFaceSettingEnabledForUser.get(user) - + ", mLockIconPressed: " + mLockIconPressed - + ", strongAuthAllowsScanning: " + strongAuthAllowsScanning - + ", isPrimaryUser: " + mIsPrimaryUser - + ", mSecureCameraLaunched: " + mSecureCameraLaunched); + Log.v(TAG, model.toString()); + } + + // Add model data to the historical buffer. + if (DEBUG_FACE && mFaceRunningState != BIOMETRIC_STATE_RUNNING + && model.isListeningForFace()) { + if (mFaceListenModels == null) { + mFaceListenModels = new ArrayDeque<>(FACE_LISTEN_CALLS_QUEUE_SIZE); + } + if (mFaceListenModels.size() >= FACE_LISTEN_CALLS_QUEUE_SIZE) { + mFaceListenModels.remove(); + } + mFaceListenModels.add(model); } - return shouldListen; } /** @@ -2919,5 +2951,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab pw.println(" enabledByUser=" + mFaceSettingEnabledForUser.get(userId)); pw.println(" mSecureCameraLaunched=" + mSecureCameraLaunched); } + if (mFaceListenModels != null && !mFaceListenModels.isEmpty()) { + final SimpleDateFormat dateFormat = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US); + pw.println(" Face listen results (last " + FACE_LISTEN_CALLS_QUEUE_SIZE + " calls):"); + for (final KeyguardFaceListenModel model : mFaceListenModels) { + final String time = dateFormat.format(new Date(model.getTimeMillis())); + pw.println(" " + time + " " + model.toString()); + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 0690907e8433..8a2c101f7057 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -798,6 +798,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mBubbleIconFactory = new BubbleIconFactory(mContext); mStackView.onDisplaySizeChanged(); } + + mStackView.onLayoutDirectionChanged(); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index fee7847ee220..790d6a2070a2 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -316,6 +316,10 @@ public class BubbleExpandedView extends LinearLayout { return false; }); + + // BubbleStackView is forced LTR, but we want to respect the locale for expanded view layout + // so the Manage button appears on the right. + setLayoutDirection(LAYOUT_DIRECTION_LOCALE); } private String getBubbleKey() { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java index e12b325f5d05..8c76cda3290f 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java @@ -186,6 +186,9 @@ public class BubbleFlyoutView extends FrameLayout { } }); + // Use locale direction so the text is aligned correctly. + setLayoutDirection(LAYOUT_DIRECTION_LOCALE); + mBgPaint.setColor(mFloatingBackgroundColor); mLeftTriangleShape = diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java index 8fd20517782f..3e694b9f5d2a 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java @@ -285,7 +285,10 @@ class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.V } }); - ShortcutInfo info = b.getEntry().getRanking().getShortcutInfo(); + // If the bubble was persisted, the entry is null but it should have shortcut info + ShortcutInfo info = b.getEntry() == null + ? b.getShortcutInfo() + : b.getEntry().getRanking().getShortcutInfo(); if (info == null) { Log.d(TAG, "ShortcutInfo required to bubble but none found for " + b); } else { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index fb081e2bf904..dfa71baddb93 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -723,6 +723,12 @@ public class BubbleStackView extends FrameLayout setUpUserEducation(); + // Force LTR by default since most of the Bubbles UI is positioned manually by the user, or + // is centered. It greatly simplifies translation positioning/animations. Views that will + // actually lay out differently in RTL, such as the flyout and expanded view, will set their + // layout direction to LOCALE. + setLayoutDirection(LAYOUT_DIRECTION_LTR); + mBubbleContainer = new PhysicsAnimationLayout(context); mBubbleContainer.setActiveController(mStackAnimationController); mBubbleContainer.setElevation(elevation); @@ -961,6 +967,9 @@ public class BubbleStackView extends FrameLayout mManageSettingsIcon = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_icon); mManageSettingsText = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_name); + + // The menu itself should respect locale direction so the icons are on the correct side. + mManageMenu.setLayoutDirection(LAYOUT_DIRECTION_LOCALE); addView(mManageMenu); } @@ -1084,6 +1093,16 @@ public class BubbleStackView extends FrameLayout mShowingManage = false; } + /** Tells the views with locale-dependent layout direction to resolve the new direction. */ + public void onLayoutDirectionChanged() { + mManageMenu.resolveLayoutDirection(); + mFlyout.resolveLayoutDirection(); + + if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { + mExpandedBubble.getExpandedView().resolveLayoutDirection(); + } + } + /** Respond to the display size change by recalculating view size and location. */ public void onDisplaySizeChanged() { setUpOverflow(); @@ -2095,16 +2114,21 @@ public class BubbleStackView extends FrameLayout mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect); - // When the menu is open, it should be at these coordinates. This will make the menu's - // bottom left corner match up with the button's bottom left corner. - final float targetX = mTempRect.left; + final boolean isLtr = + getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_LTR; + + // When the menu is open, it should be at these coordinates. The menu pops out to the right + // in LTR and to the left in RTL. + final float targetX = isLtr ? mTempRect.left : mTempRect.right - mManageMenu.getWidth(); final float targetY = mTempRect.bottom - mManageMenu.getHeight(); + final float xOffsetForAnimation = (isLtr ? 1 : -1) * mManageMenu.getWidth() / 4f; + if (show) { mManageMenu.setScaleX(0.5f); mManageMenu.setScaleY(0.5f); - mManageMenu.setTranslationX(targetX - mManageMenu.getWidth() / 4); - mManageMenu.setTranslationY(targetY + mManageMenu.getHeight() / 4); + mManageMenu.setTranslationX(targetX - xOffsetForAnimation); + mManageMenu.setTranslationY(targetY + mManageMenu.getHeight() / 4f); mManageMenu.setAlpha(0f); PhysicsAnimator.getInstance(mManageMenu) @@ -2121,8 +2145,8 @@ public class BubbleStackView extends FrameLayout .spring(DynamicAnimation.ALPHA, 0f) .spring(DynamicAnimation.SCALE_X, 0.5f) .spring(DynamicAnimation.SCALE_Y, 0.5f) - .spring(DynamicAnimation.TRANSLATION_X, targetX - mManageMenu.getWidth() / 4) - .spring(DynamicAnimation.TRANSLATION_Y, targetY + mManageMenu.getHeight() / 4) + .spring(DynamicAnimation.TRANSLATION_X, targetX - xOffsetForAnimation) + .spring(DynamicAnimation.TRANSLATION_Y, targetY + mManageMenu.getHeight() / 4f) .withEndActions(() -> mManageMenu.setVisibility(View.INVISIBLE)) .start(); } diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index fd9fda3662a3..93f0c7f41ce3 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -44,6 +44,7 @@ import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager +import com.android.systemui.globalactions.GlobalActionsDialog import com.android.systemui.util.concurrency.DelayableExecutor import java.io.FileDescriptor import java.io.PrintWriter @@ -79,6 +80,7 @@ class ControlsControllerImpl @Inject constructor ( } private var userChanging: Boolean = true + private var userStructure: UserStructure private var seedingInProgress = false private val seedingCallbacks = mutableListOf<Consumer<Boolean>>() @@ -97,7 +99,7 @@ class ControlsControllerImpl @Inject constructor ( internal var auxiliaryPersistenceWrapper: AuxiliaryPersistenceWrapper init { - val userStructure = UserStructure(context, currentUser) + userStructure = UserStructure(context, currentUser) persistenceWrapper = optionalWrapper.orElseGet { ControlsFavoritePersistenceWrapper( @@ -116,7 +118,7 @@ class ControlsControllerImpl @Inject constructor ( private fun setValuesForUser(newUser: UserHandle) { Log.d(TAG, "Changing to user: $newUser") currentUser = newUser - val userStructure = UserStructure(context, currentUser) + userStructure = UserStructure(context, currentUser) persistenceWrapper.changeFileAndBackupManager( userStructure.file, BackupManager(userStructure.userContext) @@ -192,6 +194,16 @@ class ControlsControllerImpl @Inject constructor ( it.componentName }.toSet() + // When a component is uninstalled, allow seeding to happen again if the user + // reinstalls the app + val prefs = userStructure.userContext.getSharedPreferences( + GlobalActionsDialog.PREFS_CONTROLS_FILE, Context.MODE_PRIVATE) + val completedSeedingPackageSet = prefs.getStringSet( + GlobalActionsDialog.PREFS_CONTROLS_SEEDING_COMPLETED, mutableSetOf<String>()) + val favoritePackageSet = favoriteComponentSet.map { it.packageName } + prefs.edit().putStringSet(GlobalActionsDialog.PREFS_CONTROLS_SEEDING_COMPLETED, + completedSeedingPackageSet.intersect(favoritePackageSet)).apply() + var changed = false favoriteComponentSet.subtract(serviceInfoSet).forEach { changed = true diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 1b13d4a49fec..71ce36c7c166 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -183,8 +183,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency"; static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot"; - private static final String PREFS_CONTROLS_SEEDING_COMPLETED = "SeedingCompleted"; - private static final String PREFS_CONTROLS_FILE = "controls_prefs"; + public static final String PREFS_CONTROLS_SEEDING_COMPLETED = "SeedingCompleted"; + public static final String PREFS_CONTROLS_FILE = "controls_prefs"; private static final int SEEDING_MAX = 2; private final Context mContext; diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt index cce9838bb8e2..67cf21ae10b9 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt @@ -65,7 +65,8 @@ class MediaDataCombineLatest @Inject constructor( val (entry, device) = entries[key] ?: null to null if (entry != null && device != null) { val data = entry.copy(device = device) - listeners.forEach { + val listenersCopy = listeners.toSet() + listenersCopy.forEach { it.onMediaDataLoaded(key, data) } } @@ -73,7 +74,8 @@ class MediaDataCombineLatest @Inject constructor( private fun remove(key: String) { entries.remove(key)?.let { - listeners.forEach { + val listenersCopy = listeners.toSet() + listenersCopy.forEach { it.onMediaDataRemoved(key) } } diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java index f08429bb0696..3fd9ee9a330b 100644 --- a/packages/Tethering/src/android/net/ip/IpServer.java +++ b/packages/Tethering/src/android/net/ip/IpServer.java @@ -730,12 +730,7 @@ public class IpServer extends StateMachine { final String upstreamIface = v6only.getInterfaceName(); params = new RaParams(); - // When BPF offload is enabled, we advertise an mtu lower by 16, which is the closest - // multiple of 8 >= 14, the ethernet header size. This makes kernel ebpf tethering - // offload happy. This hack should be reverted once we have the kernel fixed up. - // Note: this will automatically clamp to at least 1280 (ipv6 minimum mtu) - // see RouterAdvertisementDaemon.java putMtu() - params.mtu = mUsingBpfOffload ? v6only.getMtu() - 16 : v6only.getMtu(); + params.mtu = v6only.getMtu(); params.hasDefaultRoute = v6only.hasIpv6DefaultRoute(); if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface, ttlAdjustment); diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java index e2330ca6ffe9..0ec8654f2a20 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java +++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java @@ -75,9 +75,7 @@ final class AutofillInlineSessionController { @NonNull Consumer<InlineSuggestionsRequest> requestConsumer, @NonNull Bundle uiExtras) { // TODO(b/151123764): rename the method to better reflect what it does. if (mSession != null) { - // Send an empty response to IME and destroy the existing session. - mSession.onInlineSuggestionsResponseLocked( - InlineFillUi.emptyUi(mSession.getAutofillIdLocked())); + // Destroy the existing session. mSession.destroySessionLocked(); mInlineFillUi = null; } diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 3c0d880916ee..0d4efed25da3 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -954,7 +954,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } /** - * Call IBluetooth.onLeServiceUp() to continue if Bluetooth should be on. + * Call IBluetooth.onLeServiceUp() to continue if Bluetooth should be on, + * call IBluetooth.onBrEdrDown() to disable if Bluetooth should be off. */ private void continueFromBleOnState() { if (DBG) { @@ -966,11 +967,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.e(TAG, "onBluetoothServiceUp: mBluetooth is null!"); return; } - if (!mEnableExternal && !isBleAppPresent() && isAirplaneModeOn()) { - // Airplane mode is turned on while enabling BLE only mode, disable - // BLE now. - disableBleScanMode(); - sendBrEdrDownCallback(); + if (!mEnableExternal && !isBleAppPresent()) { + Slog.i(TAG, "Bluetooth was disabled while enabling BLE, disable BLE now"); + mEnable = false; + mBluetooth.onBrEdrDown(); return; } if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) { diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 1634f6e62897..0ab571854c72 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1374,10 +1374,9 @@ public class ConnectivityService extends IConnectivityManager.Stub if (nri == null || net == null || !LOGD_BLOCKED_NETWORKINFO) { return; } - String action = blocked ? "BLOCKED" : "UNBLOCKED"; - log(String.format("Blocked status changed to %s for %d(%d) on netId %d", blocked, - nri.mUid, nri.request.requestId, net.netId)); - mNetworkInfoBlockingLogs.log(action + " " + nri.mUid); + final String action = blocked ? "BLOCKED" : "UNBLOCKED"; + mNetworkInfoBlockingLogs.log(String.format( + "%s %d(%d) on netId %d", action, nri.mUid, nri.request.requestId, net.netId)); } /** diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6bb10c79d382..64d30158b260 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -5264,15 +5264,17 @@ public class PackageManagerService extends IPackageManager.Stub * </ul> */ int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps, - boolean matchSystemOnly) { + boolean isImplicitImageCaptureIntentAndNotSetByDpc) { return updateFlagsForResolve(flags, userId, callingUid, - wantInstantApps, matchSystemOnly, false /*onlyExposedExplicitly*/); + wantInstantApps, false /*onlyExposedExplicitly*/, + isImplicitImageCaptureIntentAndNotSetByDpc); } int updateFlagsForResolve(int flags, int userId, int callingUid, - boolean wantInstantApps, boolean onlyExposedExplicitly, boolean matchSystemOnly) { + boolean wantInstantApps, boolean onlyExposedExplicitly, + boolean isImplicitImageCaptureIntentAndNotSetByDpc) { // Safe mode means we shouldn't match any third-party components - if (mSafeMode || matchSystemOnly) { + if (mSafeMode || isImplicitImageCaptureIntentAndNotSetByDpc) { flags |= PackageManager.MATCH_SYSTEM_ONLY; } if (getInstantAppPackageName(callingUid) != null) { @@ -6400,7 +6402,8 @@ public class PackageManagerService extends IPackageManager.Stub if (!mUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart, - intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); + isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, + flags)); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "resolve intent"); @@ -6438,7 +6441,7 @@ public class PackageManagerService extends IPackageManager.Stub final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver()); final int flags = updateFlagsForResolve( 0, userId, callingUid, false /*includeInstantApps*/, - intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); + isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, 0)); final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); synchronized (mLock) { @@ -6684,6 +6687,40 @@ public class PackageManagerService extends IPackageManager.Stub return true; } + /** + * From Android R, camera intents have to match system apps. The only exception to this is if + * the DPC has set the camera persistent preferred activity. This case was introduced + * because it is important that the DPC has the ability to set both system and non-system + * camera persistent preferred activities. + * + * @return {@code true} if the intent is a camera intent and the persistent preferred + * activity was not set by the DPC. + */ + @GuardedBy("mLock") + private boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId, + String resolvedType, int flags) { + return intent.isImplicitImageCaptureIntent() && !isPersistentPreferredActivitySetByDpm( + intent, userId, resolvedType, flags); + } + + private boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId, + String resolvedType, int flags) { + PersistentPreferredIntentResolver ppir = mSettings.mPersistentPreferredActivities + .get(userId); + //TODO(b/158003772): Remove double query + List<PersistentPreferredActivity> pprefs = ppir != null + ? ppir.queryIntent(intent, resolvedType, + (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, + userId) + : new ArrayList<>(); + for (PersistentPreferredActivity ppa : pprefs) { + if (ppa.mIsSetByDpm) { + return true; + } + } + return false; + } + @GuardedBy("mLock") private ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType, int flags, List<ResolveInfo> query, boolean debug, int userId) { @@ -6767,7 +6804,8 @@ public class PackageManagerService extends IPackageManager.Stub android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1; flags = updateFlagsForResolve( flags, userId, callingUid, false /*includeInstantApps*/, - intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); + isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, + flags)); intent = updateIntentForResolve(intent); // writer synchronized (mLock) { @@ -6980,7 +7018,8 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { int flags = updateFlagsForResolve(0, parent.id, callingUid, false /*includeInstantApps*/, - intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); + isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, parent.id, + resolvedType, 0)); CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr( intent, resolvedType, flags, sourceUserId, parent.id); return xpDomainInfo != null; @@ -7067,7 +7106,8 @@ public class PackageManagerService extends IPackageManager.Stub flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart, comp != null || pkgName != null /*onlyExposedExplicitly*/, - intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); + isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, + flags)); if (comp != null) { final List<ResolveInfo> list = new ArrayList<>(1); final ActivityInfo ai = getActivityInfo(comp, flags, userId); @@ -7856,7 +7896,8 @@ public class PackageManagerService extends IPackageManager.Stub if (!mUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/, - intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); + isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, + flags)); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent activity options"); @@ -8043,7 +8084,8 @@ public class PackageManagerService extends IPackageManager.Stub "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/, - intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); + isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, + flags)); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -8134,7 +8176,7 @@ public class PackageManagerService extends IPackageManager.Stub int userId, int callingUid) { if (!mUserManager.exists(userId)) return null; flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/, - false /* matchSystemOnly */); + false /* isImplicitImageCaptureIntentAndNotSetByDpc */); List<ResolveInfo> query = queryIntentServicesInternal( intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/); if (query != null) { @@ -8166,7 +8208,7 @@ public class PackageManagerService extends IPackageManager.Stub "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps, - false /* matchSystemOnly */); + false /* isImplicitImageCaptureIntentAndNotSetByDpc */); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -8304,7 +8346,7 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/, - false /* matchSystemOnly */); + false /* isImplicitImageCaptureIntentAndNotSetByDpc */); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -19840,7 +19882,7 @@ public class PackageManagerService extends IPackageManager.Stub } synchronized (mLock) { mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter( - new PersistentPreferredActivity(filter, activity)); + new PersistentPreferredActivity(filter, activity, true)); scheduleWritePackageRestrictionsLocked(userId); } updateDefaultHomeNotLocked(userId); diff --git a/services/core/java/com/android/server/pm/PersistentPreferredActivity.java b/services/core/java/com/android/server/pm/PersistentPreferredActivity.java index 0d4cdf9dee53..5a6fd0923f53 100644 --- a/services/core/java/com/android/server/pm/PersistentPreferredActivity.java +++ b/services/core/java/com/android/server/pm/PersistentPreferredActivity.java @@ -16,31 +16,34 @@ package com.android.server.pm; +import android.content.ComponentName; +import android.content.IntentFilter; +import android.util.Log; + import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; -import android.content.ComponentName; -import android.content.IntentFilter; -import android.util.Log; - import java.io.IOException; class PersistentPreferredActivity extends IntentFilter { private static final String ATTR_NAME = "name"; // component name private static final String ATTR_FILTER = "filter"; // filter + private static final String ATTR_SET_BY_DPM = "set-by-dpm"; // set by DPM private static final String TAG = "PersistentPreferredActivity"; private static final boolean DEBUG_FILTERS = false; final ComponentName mComponent; + final boolean mIsSetByDpm; - PersistentPreferredActivity(IntentFilter filter, ComponentName activity) { + PersistentPreferredActivity(IntentFilter filter, ComponentName activity, boolean isSetByDpm) { super(filter); mComponent = activity; + mIsSetByDpm = isSetByDpm; } PersistentPreferredActivity(XmlPullParser parser) throws XmlPullParserException, IOException { @@ -52,6 +55,8 @@ class PersistentPreferredActivity extends IntentFilter { "Bad activity name " + shortComponent + " at " + parser.getPositionDescription()); } + mIsSetByDpm = Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_SET_BY_DPM)); + int outerDepth = parser.getDepth(); String tagName = parser.getName(); int type; @@ -83,6 +88,7 @@ class PersistentPreferredActivity extends IntentFilter { public void writeToXml(XmlSerializer serializer) throws IOException { serializer.attribute(null, ATTR_NAME, mComponent.flattenToShortString()); + serializer.attribute(null, ATTR_SET_BY_DPM, Boolean.toString(mIsSetByDpm)); serializer.startTag(null, ATTR_FILTER); super.writeToXml(serializer); serializer.endTag(null, ATTR_FILTER); @@ -91,6 +97,7 @@ class PersistentPreferredActivity extends IntentFilter { @Override public String toString() { return "PersistentPreferredActivity{0x" + Integer.toHexString(System.identityHashCode(this)) - + " " + mComponent.flattenToShortString() + "}"; + + " " + mComponent.flattenToShortString() + + ", mIsSetByDpm=" + mIsSetByDpm + "}"; } } diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 3ec139763e80..0c42ff6be520 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -2419,6 +2419,9 @@ public class ShortcutService extends IShortcutService.Stub { @Override public ParceledListSlice<ShortcutManager.ShareShortcutInfo> getShareTargets(String packageName, IntentFilter filter, @UserIdInt int userId) { + Preconditions.checkStringNotEmpty(packageName, "packageName"); + Objects.requireNonNull(filter, "intentFilter"); + verifyCaller(packageName, userId); enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS, "getShareTargets"); diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 79805e3b42ae..8ccf837f64dc 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -77,7 +77,11 @@ import com.android.server.pm.parsing.pkg.AndroidPackageUtils; import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.rollback.WatchdogRollbackLogger; +import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -102,6 +106,9 @@ public class StagingManager { private final PreRebootVerificationHandler mPreRebootVerificationHandler; private final Supplier<PackageParser2> mPackageParserSupplier; + private final File mFailureReasonFile = new File("/metadata/staged-install/failure_reason.txt"); + private String mFailureReason; + @GuardedBy("mStagedSessions") private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>(); @@ -125,6 +132,12 @@ public class StagingManager { mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mPreRebootVerificationHandler = new PreRebootVerificationHandler( BackgroundThread.get().getLooper()); + + if (mFailureReasonFile.exists()) { + try (BufferedReader reader = new BufferedReader(new FileReader(mFailureReasonFile))) { + mFailureReason = reader.readLine(); + } catch (Exception ignore) { } + } } /** @@ -383,10 +396,19 @@ public class StagingManager { } // Reverts apex sessions and user data (if checkpoint is supported). Also reboots the device. - private void abortCheckpoint(String errorMsg) { - Slog.e(TAG, "Aborting checkpoint: " + errorMsg); + private void abortCheckpoint(int sessionId, String errorMsg) { + String failureReason = "Failed to install sessionId: " + sessionId + " Error: " + errorMsg; + Slog.e(TAG, failureReason); try { if (supportsCheckpoint() && needsCheckpoint()) { + // Store failure reason for next reboot + try (BufferedWriter writer = + new BufferedWriter(new FileWriter(mFailureReasonFile))) { + writer.write(failureReason); + } catch (Exception e) { + Slog.w(TAG, "Failed to save failure reason: ", e); + } + // Only revert apex sessions if device supports updating apex if (mApexManager.isApexSupported()) { mApexManager.revertActiveSessions(); @@ -592,14 +614,12 @@ public class StagingManager { // If checkpoint is supported, then we only resume sessions if we are in checkpointing // mode. If not, we fail all sessions. if (supportsCheckpoint() && !needsCheckpoint()) { - // TODO(b/146343545): Persist failure reason across checkpoint reboot - Slog.d(TAG, "Reverting back to safe state. Marking " + session.sessionId - + " as failed."); - String errorMsg = "Reverting back to safe state"; - if (!TextUtils.isEmpty(mNativeFailureReason)) { - errorMsg = "Entered fs-rollback mode and reverted session due to crashing " - + "native process: " + mNativeFailureReason; + String errorMsg = "Reverting back to safe state. Marking " + session.sessionId + + " as failed"; + if (!TextUtils.isEmpty(mFailureReason)) { + errorMsg = errorMsg + ": " + mFailureReason; } + Slog.d(TAG, errorMsg); session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, errorMsg); return; } @@ -624,7 +644,7 @@ public class StagingManager { + "supposed to be activated"; session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg); - abortCheckpoint(errorMsg); + abortCheckpoint(session.sessionId, errorMsg); return; } if (isApexSessionFailed(apexSessionInfo)) { @@ -636,7 +656,7 @@ public class StagingManager { } session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg); - abortCheckpoint(errorMsg); + abortCheckpoint(session.sessionId, errorMsg); return; } if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) { @@ -647,7 +667,7 @@ public class StagingManager { + "didn't activate nor fail. Marking it as failed anyway."; session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg); - abortCheckpoint(errorMsg); + abortCheckpoint(session.sessionId, errorMsg); return; } } @@ -664,7 +684,7 @@ public class StagingManager { installApksInSession(session); } catch (PackageManagerException e) { session.setStagedSessionFailed(e.error, e.getMessage()); - abortCheckpoint(e.getMessage()); + abortCheckpoint(session.sessionId, e.getMessage()); // If checkpoint is not supported, we have to handle failure for one staged session. if (!hasApex) { @@ -1189,6 +1209,8 @@ public class StagingManager { ctx.unregisterReceiver(this); } }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); + + mFailureReasonFile.delete(); } private static class LocalIntentReceiverAsync { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index b0d4d957fc21..0a1d236cabc7 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -26,6 +26,7 @@ import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISCOURAGED; import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; +import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE; import static android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME; import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; @@ -1804,8 +1805,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { continue; } - // If this permission was granted by default, make sure it is. - if ((oldFlags & FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0) { + // If this permission was granted by default or role, make sure it is. + if ((oldFlags & FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0 + || (oldFlags & FLAG_PERMISSION_GRANTED_BY_ROLE) != 0) { // PermissionPolicyService will handle the app op for runtime permissions later. grantRuntimePermissionInternal(permName, packageName, false, Process.SYSTEM_UID, userId, delayingPermCallback); diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 6c1ff728e6b9..ab459fdadd21 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -3319,8 +3319,8 @@ public class StatsPullAtomService extends SystemService { public void run() { try { estimateAppOpsSamplingRate(); - } catch (Exception e) { - Slog.e(TAG, "AppOps sampling ratio estimation failed"); + } catch (Throwable e) { + Slog.e(TAG, "AppOps sampling ratio estimation failed: ", e); synchronized (mAppOpsSamplingRateLock) { mAppOpsSamplingRate = min(mAppOpsSamplingRate, 10); } @@ -3361,7 +3361,7 @@ public class StatsPullAtomService extends SystemService { Instant.now().minus(1, ChronoUnit.DAYS).toEpochMilli(), Long.MAX_VALUE).setFlags( OP_FLAGS_PULLED).build(); - appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete); + appOps.getHistoricalOps(histOpsRequest, AsyncTask.THREAD_POOL_EXECUTOR, ops::complete); HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); List<AppOpEntry> opsList = diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index 9476e9260c73..c38d649ada9b 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -77,6 +77,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.Xml; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; @@ -122,6 +123,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { PackageManagerInternal mPmInternal; /** File storing persisted {@link #mGrantedUriPermissions}. */ + @GuardedBy("mLock") private final AtomicFile mGrantFile; /** XML constants used in {@link #mGrantFile} */ @@ -142,6 +144,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { * This optimized lookup structure maps from {@link UriPermission#targetUid} * to {@link UriPermission#uri} to {@link UriPermission}. */ + @GuardedBy("mLock") private final SparseArray<ArrayMap<GrantUri, UriPermission>> mGrantedUriPermissions = new SparseArray<>(); @@ -206,39 +209,44 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { } } + @Override + public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg, + Uri uri, final int modeFlags, int sourceUserId, int targetUserId) { + grantUriPermissionFromOwnerUnlocked(token, fromUid, targetPkg, uri, modeFlags, sourceUserId, + targetUserId); + } + /** * @param uri This uri must NOT contain an embedded userId. * @param sourceUserId The userId in which the uri is to be resolved. * @param targetUserId The userId of the app that receives the grant. */ - @Override - public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg, Uri uri, - final int modeFlags, int sourceUserId, int targetUserId) { + private void grantUriPermissionFromOwnerUnlocked(IBinder token, int fromUid, String targetPkg, + Uri uri, final int modeFlags, int sourceUserId, int targetUserId) { targetUserId = mAmInternal.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), targetUserId, false, ALLOW_FULL_ONLY, "grantUriPermissionFromOwner", null); - synchronized(mLock) { - UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token); - if (owner == null) { - throw new IllegalArgumentException("Unknown owner: " + token); - } - if (fromUid != Binder.getCallingUid()) { - if (Binder.getCallingUid() != myUid()) { - // Only system code can grant URI permissions on behalf - // of other users. - throw new SecurityException("nice try"); - } - } - if (targetPkg == null) { - throw new IllegalArgumentException("null target"); - } - if (uri == null) { - throw new IllegalArgumentException("null uri"); - } - grantUriPermission(fromUid, targetPkg, new GrantUri(sourceUserId, uri, modeFlags), - modeFlags, owner, targetUserId); + UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token); + if (owner == null) { + throw new IllegalArgumentException("Unknown owner: " + token); } + if (fromUid != Binder.getCallingUid()) { + if (Binder.getCallingUid() != myUid()) { + // Only system code can grant URI permissions on behalf + // of other users. + throw new SecurityException("nice try"); + } + } + if (targetPkg == null) { + throw new IllegalArgumentException("null target"); + } + if (uri == null) { + throw new IllegalArgumentException("null uri"); + } + + grantUriPermissionUnlocked(fromUid, targetPkg, new GrantUri(sourceUserId, uri, modeFlags), + modeFlags, owner, targetUserId); } @Override @@ -362,7 +370,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { persistChanged |= prefixPerm.takePersistableModes(modeFlags); } - persistChanged |= maybePrunePersistedUriGrants(uid); + persistChanged |= maybePrunePersistedUriGrantsLocked(uid); if (persistChanged) { schedulePersistUriGrants(); @@ -374,8 +382,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { public void clearGrantedUriPermissions(String packageName, int userId) { mAmInternal.enforceCallingPermission( CLEAR_APP_GRANTED_URI_PERMISSIONS, "clearGrantedUriPermissions"); - synchronized(mLock) { - removeUriPermissionsForPackage(packageName, userId, true, true); + synchronized (mLock) { + removeUriPermissionsForPackageLocked(packageName, userId, true, true); } } @@ -416,11 +424,11 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { if (exactPerm != null) { persistChanged |= exactPerm.releasePersistableModes(modeFlags); - removeUriPermissionIfNeeded(exactPerm); + removeUriPermissionIfNeededLocked(exactPerm); } if (prefixPerm != null) { persistChanged |= prefixPerm.releasePersistableModes(modeFlags); - removeUriPermissionIfNeeded(prefixPerm); + removeUriPermissionIfNeededLocked(prefixPerm); } if (persistChanged) { @@ -441,8 +449,9 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { * @param targetOnly When {@code true}, only remove permissions where the app is the target, * not source. */ - void removeUriPermissionsForPackage( - String packageName, int userHandle, boolean persistable, boolean targetOnly) { + @GuardedBy("mLock") + private void removeUriPermissionsForPackageLocked(String packageName, int userHandle, + boolean persistable, boolean targetOnly) { if (userHandle == UserHandle.USER_ALL && packageName == null) { throw new IllegalArgumentException("Must narrow by either package or user"); } @@ -494,7 +503,9 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { } /** Returns if the ContentProvider has granted a uri to callingUid */ - boolean checkAuthorityGrants(int callingUid, ProviderInfo cpi, int userId, boolean checkUser) { + @GuardedBy("mLock") + private boolean checkAuthorityGrantsLocked(int callingUid, ProviderInfo cpi, int userId, + boolean checkUser) { final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid); if (perms != null) { for (int i = perms.size() - 1; i >= 0; i--) { @@ -530,7 +541,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { * * @return if any mutations occured that require persisting. */ - private boolean maybePrunePersistedUriGrants(int uid) { + @GuardedBy("mLock") + private boolean maybePrunePersistedUriGrantsLocked(int uid) { final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(uid); if (perms == null) return false; if (perms.size() < MAX_PERSISTED_URI_GRANTS) return false; @@ -552,14 +564,14 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { if (DEBUG) Slog.v(TAG, "Trimming grant created at " + perm.persistedCreateTime); perm.releasePersistableModes(~0); - removeUriPermissionIfNeeded(perm); + removeUriPermissionIfNeededLocked(perm); } return true; } /** Like checkGrantUriPermission, but takes an Intent. */ - NeededUriGrants checkGrantUriPermissionFromIntent(int callingUid, + private NeededUriGrants checkGrantUriPermissionFromIntentUnlocked(int callingUid, String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId) { if (DEBUG) Slog.v(TAG, "Checking URI perm to data=" + (intent != null ? intent.getData() : null) @@ -598,7 +610,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { } if (data != null) { GrantUri grantUri = GrantUri.resolve(contentUserHint, data, mode); - targetUid = checkGrantUriPermission(callingUid, targetPkg, grantUri, mode, targetUid); + targetUid = checkGrantUriPermissionUnlocked(callingUid, targetPkg, grantUri, mode, + targetUid); if (targetUid > 0) { if (needed == null) { needed = new NeededUriGrants(targetPkg, targetUid, mode); @@ -611,7 +624,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { Uri uri = clip.getItemAt(i).getUri(); if (uri != null) { GrantUri grantUri = GrantUri.resolve(contentUserHint, uri, mode); - targetUid = checkGrantUriPermission(callingUid, targetPkg, + targetUid = checkGrantUriPermissionUnlocked(callingUid, targetPkg, grantUri, mode, targetUid); if (targetUid > 0) { if (needed == null) { @@ -622,7 +635,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { } else { Intent clipIntent = clip.getItemAt(i).getIntent(); if (clipIntent != null) { - NeededUriGrants newNeeded = checkGrantUriPermissionFromIntent( + NeededUriGrants newNeeded = checkGrantUriPermissionFromIntentUnlocked( callingUid, targetPkg, clipIntent, mode, needed, targetUserId); if (newNeeded != null) { needed = newNeeded; @@ -635,7 +648,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { return needed; } - void readGrantedUriPermissions() { + @GuardedBy("mLock") + private void readGrantedUriPermissionsLocked() { if (DEBUG) Slog.v(TAG, "readGrantedUriPermissions()"); final long now = System.currentTimeMillis(); @@ -681,7 +695,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { if (targetUid != -1) { final GrantUri grantUri = new GrantUri(sourceUserId, uri, prefix ? Intent.FLAG_GRANT_PREFIX_URI_PERMISSION : 0); - final UriPermission perm = findOrCreateUriPermission( + final UriPermission perm = findOrCreateUriPermissionLocked( sourcePkg, targetPkg, targetUid, grantUri); perm.initPersistedModes(modeFlags, createdTime); } @@ -703,7 +717,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { } } - private UriPermission findOrCreateUriPermission(String sourcePkg, + @GuardedBy("mLock") + private UriPermission findOrCreateUriPermissionLocked(String sourcePkg, String targetPkg, int targetUid, GrantUri grantUri) { ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid); if (targetUris == null) { @@ -740,15 +755,18 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { return; } - final UriPermission perm = findOrCreateUriPermission( - pi.packageName, targetPkg, targetUid, grantUri); + final UriPermission perm; + synchronized (mLock) { + perm = findOrCreateUriPermissionLocked(pi.packageName, targetPkg, targetUid, grantUri); + } perm.grantModes(modeFlags, owner); mPmInternal.grantImplicitAccess(UserHandle.getUserId(targetUid), null, UserHandle.getAppId(targetUid), pi.applicationInfo.uid, false /*direct*/); } /** Like grantUriPermissionUnchecked, but takes an Intent. */ - void grantUriPermissionUncheckedFromIntent(NeededUriGrants needed, UriPermissionOwner owner) { + private void grantUriPermissionUncheckedFromIntent(NeededUriGrants needed, + UriPermissionOwner owner) { if (needed == null) { return; } @@ -759,7 +777,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { } } - void grantUriPermission(int callingUid, String targetPkg, GrantUri grantUri, + private void grantUriPermissionUnlocked(int callingUid, String targetPkg, GrantUri grantUri, final int modeFlags, UriPermissionOwner owner, int targetUserId) { if (targetPkg == null) { throw new NullPointerException("targetPkg"); @@ -767,7 +785,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { int targetUid = mPmInternal.getPackageUidInternal(targetPkg, MATCH_DEBUG_TRIAGED_MISSING, targetUserId); - targetUid = checkGrantUriPermission(callingUid, targetPkg, grantUri, modeFlags, targetUid); + targetUid = checkGrantUriPermissionUnlocked(callingUid, targetPkg, grantUri, modeFlags, + targetUid); if (targetUid < 0) { return; } @@ -775,7 +794,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { grantUriPermissionUnchecked(targetUid, targetPkg, grantUri, modeFlags, owner); } - void revokeUriPermission(String targetPackage, int callingUid, GrantUri grantUri, + private void revokeUriPermission(String targetPackage, int callingUid, GrantUri grantUri, final int modeFlags) { if (DEBUG) Slog.v(TAG, "Revoking all granted permissions to " + grantUri); @@ -788,8 +807,19 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { return; } + final boolean callerHoldsPermissions = checkHoldingPermissionsUnlocked(pi, grantUri, + callingUid, modeFlags); + synchronized (mLock) { + revokeUriPermissionLocked(targetPackage, callingUid, grantUri, modeFlags, + callerHoldsPermissions); + } + } + + @GuardedBy("mLock") + private void revokeUriPermissionLocked(String targetPackage, int callingUid, GrantUri grantUri, + final int modeFlags, final boolean callerHoldsPermissions) { // Does the caller have this permission on the URI? - if (!checkHoldingPermissions(pi, grantUri, callingUid, modeFlags)) { + if (!callerHoldsPermissions) { // If they don't have direct access to the URI, then revoke any // ownerless URI permissions that have been granted to them. final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid); @@ -861,7 +891,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { * the given {@link ProviderInfo}. Final permission checking is always done * in {@link ContentProvider}. */ - private boolean checkHoldingPermissions( + private boolean checkHoldingPermissionsUnlocked( ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) { if (DEBUG) Slog.v(TAG, "checkHoldingPermissions: uri=" + grantUri + " uid=" + uid); if (UserHandle.getUserId(uid) != grantUri.sourceUserId) { @@ -870,11 +900,17 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { return false; } } - return checkHoldingPermissionsInternal(pi, grantUri, uid, modeFlags, true); + return checkHoldingPermissionsInternalUnlocked(pi, grantUri, uid, modeFlags, true); } - private boolean checkHoldingPermissionsInternal(ProviderInfo pi, + private boolean checkHoldingPermissionsInternalUnlocked(ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags, boolean considerUidPermissions) { + // We must never hold our local mLock in this method, since we may need + // to call into ActivityManager for dynamic permission checks + if (Thread.holdsLock(mLock)) { + throw new IllegalStateException("Must never hold local mLock"); + } + if (pi.applicationInfo.uid == uid) { return true; } else if (!pi.exported) { @@ -968,7 +1004,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { return readMet && writeMet && forceMet; } - private void removeUriPermissionIfNeeded(UriPermission perm) { + @GuardedBy("mLock") + private void removeUriPermissionIfNeededLocked(UriPermission perm) { if (perm.modeFlags != 0) { return; } @@ -985,6 +1022,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { } } + @GuardedBy("mLock") private UriPermission findUriPermissionLocked(int targetUid, GrantUri grantUri) { final ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid); if (targetUris != null) { @@ -1020,7 +1058,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { * If you already know the uid of the target, you can supply it in * lastTargetUid else set that to -1. */ - int checkGrantUriPermission(int callingUid, String targetPkg, GrantUri grantUri, + private int checkGrantUriPermissionUnlocked(int callingUid, String targetPkg, GrantUri grantUri, int modeFlags, int lastTargetUid) { if (!Intent.isAccessUriMode(modeFlags)) { return -1; @@ -1076,7 +1114,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { boolean targetHoldsPermission = false; if (targetUid >= 0) { // First... does the target actually need this permission? - if (checkHoldingPermissions(pi, grantUri, targetUid, modeFlags)) { + if (checkHoldingPermissionsUnlocked(pi, grantUri, targetUid, modeFlags)) { // No need to grant the target this permission. if (DEBUG) Slog.v(TAG, "Target " + targetPkg + " already has full permission to " + grantUri); @@ -1144,7 +1182,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { */ boolean specialCrossUserGrant = targetUid >= 0 && UserHandle.getUserId(targetUid) != grantUri.sourceUserId - && checkHoldingPermissionsInternal(pi, grantUri, callingUid, + && checkHoldingPermissionsInternalUnlocked(pi, grantUri, callingUid, modeFlags, false /*without considering the uid permissions*/); // Second... is the provider allowing granting of URI permissions? @@ -1179,9 +1217,13 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { } // Third... does the caller itself have permission to access this uri? - if (!checkHoldingPermissions(pi, grantUri, callingUid, modeFlags)) { + if (!checkHoldingPermissionsUnlocked(pi, grantUri, callingUid, modeFlags)) { // Require they hold a strong enough Uri permission - if (!checkUriPermission(grantUri, callingUid, modeFlags)) { + final boolean res; + synchronized (mLock) { + res = checkUriPermissionLocked(grantUri, callingUid, modeFlags); + } + if (!res) { if (android.Manifest.permission.MANAGE_DOCUMENTS.equals(pi.readPermission)) { throw new SecurityException( "UID " + callingUid + " does not have permission to " + grantUri @@ -1200,13 +1242,14 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { /** * @param userId The userId in which the uri is to be resolved. */ - int checkGrantUriPermission(int callingUid, String targetPkg, Uri uri, int modeFlags, - int userId) { - return checkGrantUriPermission(callingUid, targetPkg, + private int checkGrantUriPermissionUnlocked(int callingUid, String targetPkg, Uri uri, + int modeFlags, int userId) { + return checkGrantUriPermissionUnlocked(callingUid, targetPkg, new GrantUri(userId, uri, modeFlags), modeFlags, -1); } - boolean checkUriPermission(GrantUri grantUri, int uid, final int modeFlags) { + @GuardedBy("mLock") + private boolean checkUriPermissionLocked(GrantUri grantUri, int uid, final int modeFlags) { final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0; final int minStrength = persistable ? UriPermission.STRENGTH_PERSISTABLE : UriPermission.STRENGTH_OWNED; @@ -1238,7 +1281,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { return false; } - private void writeGrantedUriPermissions() { + @GuardedBy("mLock") + private void writeGrantedUriPermissionsLocked() { if (DEBUG) Slog.v(TAG, "writeGrantedUriPermissions()"); final long startTime = SystemClock.uptimeMillis(); @@ -1299,34 +1343,35 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { public void handleMessage(Message msg) { switch (msg.what) { case PERSIST_URI_GRANTS_MSG: { - writeGrantedUriPermissions(); + synchronized (mLock) { + writeGrantedUriPermissionsLocked(); + } break; } } } } - final class LocalService implements UriGrantsManagerInternal { + private final class LocalService implements UriGrantsManagerInternal { @Override public void removeUriPermissionIfNeeded(UriPermission perm) { synchronized (mLock) { - UriGrantsManagerService.this.removeUriPermissionIfNeeded(perm); + UriGrantsManagerService.this.removeUriPermissionIfNeededLocked(perm); } } @Override public void revokeUriPermission(String targetPackage, int callingUid, GrantUri grantUri, int modeFlags) { - synchronized (mLock) { - UriGrantsManagerService.this.revokeUriPermission( - targetPackage, callingUid, grantUri, modeFlags); - } + UriGrantsManagerService.this.revokeUriPermission( + targetPackage, callingUid, grantUri, modeFlags); } @Override public boolean checkUriPermission(GrantUri grantUri, int uid, int modeFlags) { synchronized (mLock) { - return UriGrantsManagerService.this.checkUriPermission(grantUri, uid, modeFlags); + return UriGrantsManagerService.this.checkUriPermissionLocked(grantUri, uid, + modeFlags); } } @@ -1334,83 +1379,73 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { public int checkGrantUriPermission(int callingUid, String targetPkg, Uri uri, int modeFlags, int userId) { enforceNotIsolatedCaller("checkGrantUriPermission"); - synchronized (mLock) { - return UriGrantsManagerService.this.checkGrantUriPermission( - callingUid, targetPkg, uri, modeFlags, userId); - } + return UriGrantsManagerService.this.checkGrantUriPermissionUnlocked( + callingUid, targetPkg, uri, modeFlags, userId); } @Override public NeededUriGrants checkGrantUriPermissionFromIntent(Intent intent, int callingUid, String targetPkg, int targetUserId) { - synchronized (mLock) { - final int mode = (intent != null) ? intent.getFlags() : 0; - return UriGrantsManagerService.this.checkGrantUriPermissionFromIntent( - callingUid, targetPkg, intent, mode, null, targetUserId); - } + final int mode = (intent != null) ? intent.getFlags() : 0; + return UriGrantsManagerService.this.checkGrantUriPermissionFromIntentUnlocked( + callingUid, targetPkg, intent, mode, null, targetUserId); } @Override public void grantUriPermissionUncheckedFromIntent(NeededUriGrants needed, UriPermissionOwner owner) { - synchronized (mLock) { - UriGrantsManagerService.this.grantUriPermissionUncheckedFromIntent(needed, owner); - } + UriGrantsManagerService.this.grantUriPermissionUncheckedFromIntent(needed, owner); } @Override public void onSystemReady() { synchronized (mLock) { - UriGrantsManagerService.this.readGrantedUriPermissions(); + UriGrantsManagerService.this.readGrantedUriPermissionsLocked(); } } @Override public IBinder newUriPermissionOwner(String name) { enforceNotIsolatedCaller("newUriPermissionOwner"); - synchronized(mLock) { - UriPermissionOwner owner = new UriPermissionOwner(this, name); - return owner.getExternalToken(); - } + UriPermissionOwner owner = new UriPermissionOwner(this, name); + return owner.getExternalToken(); } @Override public void removeUriPermissionsForPackage(String packageName, int userHandle, boolean persistable, boolean targetOnly) { - synchronized(mLock) { - UriGrantsManagerService.this.removeUriPermissionsForPackage( + synchronized (mLock) { + UriGrantsManagerService.this.removeUriPermissionsForPackageLocked( packageName, userHandle, persistable, targetOnly); } } @Override public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId) { - synchronized(mLock) { - final UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token); - if (owner == null) { - throw new IllegalArgumentException("Unknown owner: " + token); - } + final UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token); + if (owner == null) { + throw new IllegalArgumentException("Unknown owner: " + token); + } - if (uri == null) { - owner.removeUriPermissions(mode); - } else { - owner.removeUriPermission(new GrantUri(userId, uri, mode), mode); - } + if (uri == null) { + owner.removeUriPermissions(mode); + } else { + owner.removeUriPermission(new GrantUri(userId, uri, mode), mode); } } @Override public boolean checkAuthorityGrants(int callingUid, ProviderInfo cpi, int userId, boolean checkUser) { - synchronized(mLock) { - return UriGrantsManagerService.this.checkAuthorityGrants( + synchronized (mLock) { + return UriGrantsManagerService.this.checkAuthorityGrantsLocked( callingUid, cpi, userId, checkUser); } } @Override public void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) { - synchronized(mLock) { + synchronized (mLock) { boolean needSep = false; boolean printedAnything = false; if (mGrantedUriPermissions.size() > 0) { diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 7f03778ab1c7..0d5621dc0e4f 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -23,7 +23,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.WindowConfiguration.activityTypeToString; import static android.app.WindowConfiguration.windowingModeToString; @@ -269,9 +268,6 @@ class ActivityStack extends Task { private final AnimatingActivityRegistry mAnimatingActivityRegistry = new AnimatingActivityRegistry(); - /** Stores the override windowing-mode from before a transient mode change (eg. split) */ - private int mRestoreOverrideWindowingMode = WINDOWING_MODE_UNDEFINED; - private boolean mTopActivityOccludesKeyguard; private ActivityRecord mTopDismissingKeyguardActivity; @@ -662,19 +658,6 @@ class ActivityStack extends Task { } /** - * A transient windowing mode is one which activities enter into temporarily. Examples of this - * are Split window modes and pip. Non-transient modes are modes that displays can adopt. - * - * @param windowingMode the windowingMode to test for transient-ness. - * @return {@code true} if the windowing mode is transient, {@code false} otherwise. - */ - private static boolean isTransientWindowingMode(int windowingMode) { - return windowingMode == WINDOWING_MODE_PINNED - || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY - || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; - } - - /** * Specialization of {@link #setWindowingMode(int)} for this subclass. * * @param preferredWindowingMode the preferred windowing mode. This may not be honored depending @@ -698,11 +681,6 @@ class ActivityStack extends Task { final int currentOverrideMode = getRequestedOverrideWindowingMode(); final Task topTask = getTopMostTask(); int windowingMode = preferredWindowingMode; - if (preferredWindowingMode == WINDOWING_MODE_UNDEFINED - && isTransientWindowingMode(currentMode)) { - // Leaving a transient mode. Interpret UNDEFINED as "restore" - windowingMode = mRestoreOverrideWindowingMode; - } // Need to make sure windowing mode is supported. If we in the process of creating the stack // no need to resolve the windowing mode again as it is already resolved to the right mode. @@ -712,29 +690,16 @@ class ActivityStack extends Task { windowingMode = WINDOWING_MODE_UNDEFINED; } } - if (taskDisplayArea.getRootSplitScreenPrimaryTask() == this - && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) { - // Resolution to split-screen secondary for the primary split-screen stack means - // we want to leave split-screen mode. - windowingMode = mRestoreOverrideWindowingMode; - } final boolean alreadyInSplitScreenMode = taskDisplayArea.isSplitScreenModeActivated(); - // Take any required action due to us not supporting the preferred windowing mode. - if (alreadyInSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN + if (creating && alreadyInSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN && isActivityTypeStandardOrUndefined()) { - final boolean preferredSplitScreen = - preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY - || preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; - if (preferredSplitScreen || creating) { - // Looks like we can't launch in split screen mode or the stack we are launching - // doesn't support split-screen mode, go ahead an dismiss split-screen and display a - // warning toast about it. - mAtmService.getTaskChangeNotificationController() - .notifyActivityDismissingDockedStack(); - taskDisplayArea.onSplitScreenModeDismissed(this); - } + // If the stack is being created explicitly in fullscreen mode, dismiss split-screen + // and display a warning toast about it. + mAtmService.getTaskChangeNotificationController() + .notifyActivityDismissingDockedStack(); + taskDisplayArea.onSplitScreenModeDismissed(this); } if (currentMode == windowingMode) { @@ -797,9 +762,6 @@ class ActivityStack extends Task { + " while there is already one isn't currently supported"); //return; } - if (isTransientWindowingMode(windowingMode) && !isTransientWindowingMode(currentMode)) { - mRestoreOverrideWindowingMode = currentOverrideMode; - } mTmpRect2.setEmpty(); if (windowingMode != WINDOWING_MODE_FULLSCREEN) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 59181a64f423..a5b94b327699 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -4776,6 +4776,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * @param sc The new SurfaceControl, where the DisplayContent's surfaces will be re-parented to. */ void reparentDisplayContent(WindowState win, SurfaceControl sc) { + if (mParentWindow != null) { + mParentWindow.removeEmbeddedDisplayContent(this); + } mParentWindow = win; mParentWindow.addEmbeddedDisplayContent(this); mParentSurfaceControl = sc; diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index d1cb2105246a..aee5a1d7838b 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -106,6 +106,8 @@ class WallpaperController { private static final int WALLPAPER_DRAW_TIMEOUT = 2; private int mWallpaperDrawState = WALLPAPER_DRAW_NORMAL; + private boolean mShouldUpdateZoom; + /** * Temporary storage for taking a screenshot of the wallpaper. * @see #screenshotWallpaperLocked() @@ -400,6 +402,7 @@ class WallpaperController { void setWallpaperZoomOut(WindowState window, float zoom) { if (Float.compare(window.mWallpaperZoomOut, zoom) != 0) { window.mWallpaperZoomOut = zoom; + mShouldUpdateZoom = true; updateWallpaperOffsetLocked(window, false); } } @@ -623,9 +626,7 @@ class WallpaperController { mLastWallpaperX = mWallpaperTarget.mWallpaperX; mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep; } - if (mWallpaperTarget.mWallpaperZoomOut >= 0) { - mLastWallpaperZoomOut = mWallpaperTarget.mWallpaperZoomOut; - } + computeLastWallpaperZoomOut(); if (mWallpaperTarget.mWallpaperY >= 0) { mLastWallpaperY = mWallpaperTarget.mWallpaperY; mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep; @@ -804,8 +805,11 @@ class WallpaperController { * we'll have conflicts and break the "depth system" mental model. */ private void computeLastWallpaperZoomOut() { - mLastWallpaperZoomOut = 0; - mDisplayContent.forAllWindows(mComputeMaxZoomOutFunction, true); + if (mShouldUpdateZoom) { + mLastWallpaperZoomOut = 0; + mDisplayContent.forAllWindows(mComputeMaxZoomOutFunction, true); + mShouldUpdateZoom = false; + } } private float zoomOutToScale(float zoom) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 045089082fd8..36232e13fcf1 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -5790,10 +5790,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // be invoked and we need to invoke it ourself. if (mLocalSyncId >= 0) { mBLASTSyncEngine.setReady(mLocalSyncId); - } else { - mWaitingListener.onTransactionReady(mWaitingSyncId, mBLASTSyncTransaction); + return mWinAnimator.finishDrawingLocked(null); } + mWaitingListener.onTransactionReady(mWaitingSyncId, mBLASTSyncTransaction); mUsingBLASTSyncTransaction = false; mWaitingSyncId = 0; diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java index e86399e1a631..62b6a65cc6cb 100644 --- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java @@ -60,14 +60,12 @@ import java.util.Set; public class UriGrantsManagerServiceTest { private UriGrantsMockContext mContext; - private UriGrantsManagerService mService; - private UriGrantsManagerInternal mLocalService; + private UriGrantsManagerInternal mService; @Before public void setUp() throws Exception { mContext = new UriGrantsMockContext(InstrumentationRegistry.getContext()); - mService = UriGrantsManagerService.createForTest(mContext.getFilesDir()); - mLocalService = mService.getLocalService(); + mService = UriGrantsManagerService.createForTest(mContext.getFilesDir()).getLocalService(); } /** @@ -80,8 +78,7 @@ public class UriGrantsManagerServiceTest { final GrantUri expectedGrant = new GrantUri(USER_PRIMARY, URI_PHOTO_1, FLAG_READ); final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent( - UID_PRIMARY_CAMERA, PKG_SOCIAL, intent, intent.getFlags(), null, - USER_PRIMARY); + intent, UID_PRIMARY_CAMERA, PKG_SOCIAL, USER_PRIMARY); assertEquals(PKG_SOCIAL, needed.targetPkg); assertEquals(UID_PRIMARY_SOCIAL, needed.targetUid); assertEquals(FLAG_READ, needed.flags); @@ -98,8 +95,7 @@ public class UriGrantsManagerServiceTest { final GrantUri expectedGrant = new GrantUri(USER_PRIMARY, URI_PHOTO_1, FLAG_READ); final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent( - UID_PRIMARY_CAMERA, PKG_SOCIAL, intent, intent.getFlags(), null, - USER_SECONDARY); + intent, UID_PRIMARY_CAMERA, PKG_SOCIAL, USER_SECONDARY); assertEquals(PKG_SOCIAL, needed.targetPkg); assertEquals(UID_SECONDARY_SOCIAL, needed.targetUid); assertEquals(FLAG_READ, needed.flags); @@ -113,8 +109,7 @@ public class UriGrantsManagerServiceTest { public void testNeeded_public() { final Intent intent = new Intent(Intent.ACTION_VIEW, URI_PUBLIC).addFlags(FLAG_READ); final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent( - UID_PRIMARY_PUBLIC, PKG_SOCIAL, intent, intent.getFlags(), null, - USER_PRIMARY); + intent, UID_PRIMARY_PUBLIC, PKG_SOCIAL, USER_PRIMARY); assertNull(needed); } @@ -128,7 +123,7 @@ public class UriGrantsManagerServiceTest { final GrantUri expectedGrant = new GrantUri(USER_PRIMARY, URI_PUBLIC, FLAG_READ); final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent( - UID_PRIMARY_PUBLIC, PKG_SOCIAL, intent, intent.getFlags(), null, USER_SECONDARY); + intent, UID_PRIMARY_PUBLIC, PKG_SOCIAL, USER_SECONDARY); assertEquals(PKG_SOCIAL, needed.targetPkg); assertEquals(UID_SECONDARY_SOCIAL, needed.targetUid); assertEquals(FLAG_READ, needed.flags); @@ -143,7 +138,7 @@ public class UriGrantsManagerServiceTest { final Intent intent = new Intent(Intent.ACTION_VIEW, URI_PRIVATE).addFlags(FLAG_READ); try { mService.checkGrantUriPermissionFromIntent( - UID_PRIMARY_PRIVATE, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY); + intent, UID_PRIMARY_PRIVATE, PKG_SOCIAL, USER_PRIMARY); fail(); } catch (SecurityException expected) { } @@ -158,7 +153,7 @@ public class UriGrantsManagerServiceTest { final Intent intent = new Intent(Intent.ACTION_VIEW, URI_FORCE) .addFlags(FLAG_READ); final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent( - UID_PRIMARY_FORCE, PKG_FORCE, intent, intent.getFlags(), null, USER_PRIMARY); + intent, UID_PRIMARY_FORCE, PKG_FORCE, USER_PRIMARY); assertEquals(asSet(new GrantUri(USER_PRIMARY, URI_FORCE, 0)), needed.uris); } @@ -172,15 +167,15 @@ public class UriGrantsManagerServiceTest { { final Intent intent = new Intent(Intent.ACTION_VIEW, uri) .addFlags(FLAG_READ | Intent.FLAG_ACTIVITY_CLEAR_TASK); - assertNull(mService.checkGrantUriPermissionFromIntent(UID_PRIMARY_COMPLEX, PKG_SOCIAL, - intent, intent.getFlags(), null, USER_PRIMARY)); + assertNull(mService.checkGrantUriPermissionFromIntent( + intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_PRIMARY)); } { final Intent intent = new Intent(Intent.ACTION_VIEW, uri) .addFlags(FLAG_READ | FLAG_PREFIX); try { - mService.checkGrantUriPermissionFromIntent(UID_PRIMARY_COMPLEX, PKG_SOCIAL, - intent, intent.getFlags(), null, USER_PRIMARY); + mService.checkGrantUriPermissionFromIntent( + intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_PRIMARY); fail(); } catch (SecurityException expected) { } @@ -189,8 +184,8 @@ public class UriGrantsManagerServiceTest { final Intent intent = new Intent(Intent.ACTION_VIEW, uri) .addFlags(FLAG_READ | FLAG_PERSISTABLE); try { - mService.checkGrantUriPermissionFromIntent(UID_PRIMARY_COMPLEX, PKG_SOCIAL, - intent, intent.getFlags(), null, USER_PRIMARY); + mService.checkGrantUriPermissionFromIntent( + intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_PRIMARY); fail(); } catch (SecurityException expected) { } @@ -209,8 +204,7 @@ public class UriGrantsManagerServiceTest { final Intent intent = new Intent(Intent.ACTION_VIEW, uri) .addFlags(FLAG_READ); final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent( - UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null, - USER_SECONDARY); + intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_SECONDARY); assertEquals(FLAG_READ, needed.flags); } { @@ -218,8 +212,7 @@ public class UriGrantsManagerServiceTest { .addFlags(FLAG_READ | FLAG_PREFIX); try { mService.checkGrantUriPermissionFromIntent( - UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null, - USER_SECONDARY); + intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_SECONDARY); fail(); } catch (SecurityException expected) { } @@ -229,8 +222,7 @@ public class UriGrantsManagerServiceTest { .addFlags(FLAG_READ | FLAG_PERSISTABLE); try { mService.checkGrantUriPermissionFromIntent( - UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null, - USER_SECONDARY); + intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_SECONDARY); fail(); } catch (SecurityException expected) { } @@ -248,21 +240,21 @@ public class UriGrantsManagerServiceTest { final Intent intent = new Intent(Intent.ACTION_VIEW, uri) .addFlags(FLAG_READ); final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent( - UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY); + intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_PRIMARY); assertEquals(asSet(new GrantUri(USER_PRIMARY, uri, 0)), needed.uris); } { final Intent intent = new Intent(Intent.ACTION_VIEW, uri) .addFlags(FLAG_READ | FLAG_PREFIX); final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent( - UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY); + intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_PRIMARY); assertEquals(asSet(new GrantUri(USER_PRIMARY, uri, FLAG_PREFIX)), needed.uris); } { final Intent intent = new Intent(Intent.ACTION_VIEW, uri) .addFlags(FLAG_READ | FLAG_PERSISTABLE); final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent( - UID_PRIMARY_COMPLEX, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY); + intent, UID_PRIMARY_COMPLEX, PKG_SOCIAL, USER_PRIMARY); assertEquals(asSet(new GrantUri(USER_PRIMARY, uri, 0)), needed.uris); } } @@ -284,8 +276,8 @@ public class UriGrantsManagerServiceTest { // When granting towards primary, persistable can't be honored so // the entire grant fails try { - mService.checkGrantUriPermissionFromIntent(UID_PRIMARY_CAMERA, PKG_SOCIAL, intent, - intent.getFlags(), null, USER_PRIMARY); + mService.checkGrantUriPermissionFromIntent( + intent, UID_PRIMARY_CAMERA, PKG_SOCIAL, USER_PRIMARY); fail(); } catch (SecurityException expected) { } @@ -294,8 +286,8 @@ public class UriGrantsManagerServiceTest { // When granting towards secondary, persistable can't be honored so // the entire grant fails try { - mService.checkGrantUriPermissionFromIntent(UID_PRIMARY_CAMERA, PKG_SOCIAL, intent, - intent.getFlags(), null, USER_SECONDARY); + mService.checkGrantUriPermissionFromIntent( + intent, UID_PRIMARY_CAMERA, PKG_SOCIAL, USER_SECONDARY); fail(); } catch (SecurityException expected) { } @@ -310,18 +302,16 @@ public class UriGrantsManagerServiceTest { public void testGrant_overlap() { final Intent intent = new Intent(Intent.ACTION_VIEW, URI_PHOTO_1).addFlags(FLAG_READ); - final UriPermissionOwner activity = new UriPermissionOwner(mLocalService, "activity"); - final UriPermissionOwner service = new UriPermissionOwner(mLocalService, "service"); + final UriPermissionOwner activity = new UriPermissionOwner(mService, "activity"); + final UriPermissionOwner service = new UriPermissionOwner(mService, "service"); final GrantUri expectedGrant = new GrantUri(USER_PRIMARY, URI_PHOTO_1, FLAG_READ); // Grant read via activity and write via service mService.grantUriPermissionUncheckedFromIntent(mService.checkGrantUriPermissionFromIntent( - UID_PRIMARY_CAMERA, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY), - activity); + intent, UID_PRIMARY_CAMERA, PKG_SOCIAL, USER_PRIMARY), activity); mService.grantUriPermissionUncheckedFromIntent(mService.checkGrantUriPermissionFromIntent( - UID_PRIMARY_CAMERA, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY), - service); + intent, UID_PRIMARY_CAMERA, PKG_SOCIAL, USER_PRIMARY), service); // Verify that everything is good with the world assertTrue(mService.checkUriPermission(expectedGrant, UID_PRIMARY_SOCIAL, FLAG_READ)); @@ -338,7 +328,7 @@ public class UriGrantsManagerServiceTest { @Test public void testCheckAuthorityGrants() { final Intent intent = new Intent(Intent.ACTION_VIEW, URI_PHOTO_1).addFlags(FLAG_READ); - final UriPermissionOwner owner = new UriPermissionOwner(mLocalService, "primary"); + final UriPermissionOwner owner = new UriPermissionOwner(mService, "primary"); final ProviderInfo cameraInfo = mContext.mPmInternal.resolveContentProvider( PKG_CAMERA, 0, USER_PRIMARY); @@ -355,8 +345,7 @@ public class UriGrantsManagerServiceTest { // Granting primary camera to primary social mService.grantUriPermissionUncheckedFromIntent(mService.checkGrantUriPermissionFromIntent( - UID_PRIMARY_CAMERA, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY), - owner); + intent, UID_PRIMARY_CAMERA, PKG_SOCIAL, USER_PRIMARY), owner); assertTrue(mService.checkAuthorityGrants(UID_PRIMARY_SOCIAL, cameraInfo, USER_PRIMARY, true)); assertFalse(mService.checkAuthorityGrants(UID_PRIMARY_SOCIAL, @@ -368,8 +357,7 @@ public class UriGrantsManagerServiceTest { // Granting secondary camera to primary social mService.grantUriPermissionUncheckedFromIntent(mService.checkGrantUriPermissionFromIntent( - UID_SECONDARY_CAMERA, PKG_SOCIAL, intent, intent.getFlags(), null, USER_PRIMARY), - owner); + intent, UID_SECONDARY_CAMERA, PKG_SOCIAL, USER_PRIMARY), owner); assertTrue(mService.checkAuthorityGrants(UID_PRIMARY_SOCIAL, cameraInfo, USER_PRIMARY, true)); assertTrue(mService.checkAuthorityGrants(UID_PRIMARY_SOCIAL, diff --git a/wifi/Android.bp b/wifi/Android.bp index 83b35616286a..9c5b7b66f2a3 100644 --- a/wifi/Android.bp +++ b/wifi/Android.bp @@ -135,7 +135,6 @@ java_sdk_library { permitted_packages: [ "android.hardware.wifi", "android.net.wifi", - "android.x.net.wifi", // Created by jarjar rules. "com.android.wifi.x", ], |