Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 676fc201 authored by Anna Zhuravleva's avatar Anna Zhuravleva
Browse files

Block taking recents screenshot if private profile available

DocsUI shows separate tab when private profile is on.
User can move DocUI to the background and then lock private
profile. Recents preview screen shows screenshot
of the DocUI at the last moment it was in foreground
(when the space was unlocked). As a result Recents preview
can leak whether private profile exists on device.
This change sets setting to show blank
screen in the recents when private profile is avaialble.

Bug: 329241706
Test: manual, https://b.corp.google.com/issues/329241706#comment3
Change-Id: I05914391db7bd15a88c6043d14265682ffd8437c
parent 97fe5b16
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -75,6 +75,7 @@ import com.android.documentsui.roots.ProvidersCache;
import com.android.documentsui.sidebar.RootsFragment;
import com.android.documentsui.sorting.SortController;
import com.android.documentsui.sorting.SortModel;
import com.android.modules.utils.build.SdkLevel;

import com.google.android.material.appbar.AppBarLayout;

@@ -376,6 +377,7 @@ public abstract class BaseActivity

        // Base classes must update result in their onCreate.
        setResult(AppCompatActivity.RESULT_CANCELED);
        updateRecentsSetting();
    }

    private NavigationViewManager getNavigationViewManager(Breadcrumb breadcrumb,
@@ -1046,4 +1048,28 @@ public abstract class BaseActivity
         */
        void onDirectoryLoaded(@Nullable Uri uri);
    }

    /**
     * Updates the Recents preview settings based on presence of hidden profiles. Used not to leak
     * Private profile existence when it was locked after the app was moved to the Recents.
     */
    public void updateRecentsSetting() {
        if (!SdkLevel.isAtLeastV()) {
            return;
        }

        if (mUserManagerState == null) {
            Log.e(TAG, "Can't update Recents screenshot setting: User manager state is null.");
            return;
        }

        if (DEBUG) {
            Log.d(
                    TAG,
                    "Set recents screenshot to "
                            + (!mUserManagerState.areHiddenInQuietModeProfilesPresent() ? "enabled"
                            : "disabled"));
        }
        setRecentsScreenshotEnabled(!mUserManagerState.areHiddenInQuietModeProfilesPresent());
    }
}
+27 −4
Original line number Diff line number Diff line
@@ -89,8 +89,9 @@ public interface UserManagerState {
     * received in broadcast
     *
     * @param userId {@link UserId} for the profile for which the availability status changed
     * @param action {@link Intent}.ACTION_PROFILE_UNAVAILABLE or
     *               {@link Intent}.ACTION_PROFILE_AVAILABLE
     * @param action {@link Intent}.ACTION_PROFILE_UNAVAILABLE and {@link
     *     Intent}.ACTION_PROFILE_AVAILABLE, {@link Intent}.ACTION_PROFILE_ADDED} and {@link
     *     Intent}.ACTION_PROFILE_REMOVED}
     */
    void onProfileActionStatusChange(String action, UserId userId);

@@ -99,6 +100,9 @@ public interface UserManagerState {
     */
    void setCurrentStateIntent(Intent intent);

    /** Returns true if there are hidden profiles */
    boolean areHiddenInQuietModeProfilesPresent();

    /**
     * Creates an implementation of {@link UserManagerState}.
     */
@@ -235,11 +239,13 @@ public interface UserManagerState {
            if (userProperties.getShowInQuietMode() != UserProperties.SHOW_IN_QUIET_MODE_HIDDEN) {
                return;
            }
            if (Intent.ACTION_PROFILE_UNAVAILABLE.equals(action)) {
            if (Intent.ACTION_PROFILE_UNAVAILABLE.equals(action)
                    || Intent.ACTION_PROFILE_REMOVED.equals(action)) {
                synchronized (mUserIds) {
                    mUserIds.remove(userId);
                }
            } else if (Intent.ACTION_PROFILE_AVAILABLE.equals(action)) {
            } else if (Intent.ACTION_PROFILE_AVAILABLE.equals(action)
                    || Intent.ACTION_PROFILE_ADDED.equals(action)) {
                synchronized (mUserIds) {
                    if (!mUserIds.contains(userId)) {
                        mUserIds.add(userId);
@@ -280,6 +286,23 @@ public interface UserManagerState {
            mCurrentStateIntent = intent;
        }

        @Override
        public boolean areHiddenInQuietModeProfilesPresent() {
            if (!SdkLevel.isAtLeastV()) {
                return false;
            }

            for (UserId userId : getUserIds()) {
                if (mUserManager
                                .getUserProperties(UserHandle.of(userId.getIdentifier()))
                                .getShowInQuietMode()
                        == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN) {
                    return true;
                }
            }
            return false;
        }

        private List<UserId> getUserIdsInternal() {
            final List<UserId> result = new ArrayList<>();

+8 −3
Original line number Diff line number Diff line
@@ -262,8 +262,8 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
            if (userProperties.getShowInQuietMode()
                    == UserProperties.SHOW_IN_QUIET_MODE_PAUSED) {
                if (Objects.equal(mActivity.getSelectedUser(), userId)) {
                    // We only need to refresh the layout when the selected user is equal to the
                    // received profile user.
                    // We only need to refresh the layout when the selected user is equal to
                    // the received profile user.
                    onPausedProfileStatusChange(action, userId);
                }
                return;
@@ -308,6 +308,7 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
    }

    private void onHiddenProfileStatusChange(String action, UserId userId) {
        mActivity.updateRecentsSetting();
        if (Intent.ACTION_PROFILE_UNAVAILABLE.equals(action)) {
            if (mProviderTestRunnable != null) {
                mHandler.removeCallbacks(mProviderTestRunnable);
@@ -404,7 +405,9 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
    private boolean isProfileStatusAction(String action) {
        if (!SdkLevel.isAtLeastV()) return isManagedProfileAction(action);
        return Intent.ACTION_PROFILE_AVAILABLE.equals(action)
                || Intent.ACTION_PROFILE_UNAVAILABLE.equals(action);
                || Intent.ACTION_PROFILE_UNAVAILABLE.equals(action)
                || Intent.ACTION_PROFILE_ADDED.equals(action)
                || Intent.ACTION_PROFILE_REMOVED.equals(action);
    }

    private static boolean isManagedProfileAction(String action) {
@@ -642,6 +645,8 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
            if (SdkLevel.isAtLeastV()) {
                filter.addAction(Intent.ACTION_PROFILE_AVAILABLE);
                filter.addAction(Intent.ACTION_PROFILE_UNAVAILABLE);
                filter.addAction(Intent.ACTION_PROFILE_ADDED);
                filter.addAction(Intent.ACTION_PROFILE_REMOVED);
            }
            // DocumentsApplication will resend the broadcast locally after roots are updated.
            // Register to a local broadcast manager to avoid this fragment from updating before
+5 −0
Original line number Diff line number Diff line
@@ -84,4 +84,9 @@ public class TestUserManagerState implements UserManagerState {
    @Override
    public void setCurrentStateIntent(Intent intent) {
    }

    @Override
    public boolean areHiddenInQuietModeProfilesPresent() {
        return false;
    }
}