[1/n] Add userMinAspectRatio to packageUserState

Add per-user per-app min aspect ratio override setting that can only be
accessed by the same package.

Fix: 287447864
Test: atest CrossUserPackageVisibilityTests
      atest PackageManagerServiceTest
      atest PackageManagerSettingsTests
Change-Id: I2735a08c0830eaeee2c5f25d5f65bdbfe9890138
This commit is contained in:
Graciela Wissen Putri
2023-06-19 13:50:50 +00:00
parent 488ec4b2c8
commit 370d722565
15 changed files with 227 additions and 21 deletions

View File

@@ -770,6 +770,11 @@ interface IPackageManager {
void setSplashScreenTheme(String packageName, String themeName, int userId);
int getUserMinAspectRatio(String packageName, int userId);
@EnforcePermission("INSTALL_PACKAGES")
void setUserMinAspectRatio(String packageName, int userId, int aspectRatio);
List<String> getMimeGroup(String packageName, String group);
boolean isAutoRevokeWhitelisted(String packageName);

View File

@@ -2342,6 +2342,64 @@ public abstract class PackageManager {
*/
public static final int INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST = -130;
/**
* App minimum aspect ratio set by the user which will override app-defined aspect ratio.
*
* @hide
*/
@IntDef(prefix = { "USER_MIN_ASPECT_RATIO_" }, value = {
USER_MIN_ASPECT_RATIO_UNSET,
USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
USER_MIN_ASPECT_RATIO_DISPLAY_SIZE,
USER_MIN_ASPECT_RATIO_4_3,
USER_MIN_ASPECT_RATIO_16_9,
USER_MIN_ASPECT_RATIO_3_2,
})
@Retention(RetentionPolicy.SOURCE)
public @interface UserMinAspectRatio {}
/**
* No aspect ratio override has been set by user.
*
* @hide
*/
public static final int USER_MIN_ASPECT_RATIO_UNSET = 0;
/**
* Aspect ratio override code: user forces app to split screen aspect ratio. This is adjusted to
* half of the screen without the split screen divider.
*
* @hide
*/
public static final int USER_MIN_ASPECT_RATIO_SPLIT_SCREEN = 1;
/**
* Aspect ratio override code: user forces app to the aspect ratio of the device display size.
* This will be the portrait aspect ratio of the device if the app is portrait or the landscape
* aspect ratio of the device if the app is landscape.
*
* @hide
*/
public static final int USER_MIN_ASPECT_RATIO_DISPLAY_SIZE = 2;
/**
* Aspect ratio override code: user forces app to 4:3 min aspect ratio
* @hide
*/
public static final int USER_MIN_ASPECT_RATIO_4_3 = 3;
/**
* Aspect ratio override code: user forces app to 16:9 min aspect ratio
* @hide
*/
public static final int USER_MIN_ASPECT_RATIO_16_9 = 4;
/**
* Aspect ratio override code: user forces app to 3:2 min aspect ratio
* @hide
*/
public static final int USER_MIN_ASPECT_RATIO_3_2 = 5;
/** @hide */
@IntDef(flag = true, prefix = { "DELETE_" }, value = {
DELETE_KEEP_DATA,

View File

@@ -610,7 +610,8 @@ final class DeletePackageHelper {
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/,
null /*splashScreenTheme*/,
0 /*firstInstallTime*/);
0 /*firstInstallTime*/,
PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
}
mPm.mSettings.writeKernelMappingLPr(ps);
}

View File

@@ -30,6 +30,7 @@ import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
import static android.os.Process.INVALID_UID;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
@@ -5238,6 +5239,20 @@ public class PackageManagerService implements PackageSender, TestUtilityService
: packageState.getUserStateOrDefault(userId).getSplashScreenTheme();
}
@Override
@PackageManager.UserMinAspectRatio
public int getUserMinAspectRatio(@NonNull String packageName, int userId) {
final Computer snapshot = snapshotComputer();
final int callingUid = Binder.getCallingUid();
snapshot.enforceCrossUserPermission(
callingUid, userId, false /* requireFullPermission */,
false /* checkShell */, "getUserMinAspectRatio");
final PackageStateInternal packageState = snapshot
.getPackageStateForInstalledAndFiltered(packageName, callingUid, userId);
return packageState == null ? USER_MIN_ASPECT_RATIO_UNSET
: packageState.getUserStateOrDefault(userId).getMinAspectRatio();
}
@Override
public Bundle getSuspendedPackageAppExtras(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
@@ -6201,6 +6216,32 @@ public class PackageManagerService implements PackageSender, TestUtilityService
return true;
}
@android.annotation.EnforcePermission(android.Manifest.permission.INSTALL_PACKAGES)
@Override
public void setUserMinAspectRatio(@NonNull String packageName, int userId,
@PackageManager.UserMinAspectRatio int aspectRatio) {
setUserMinAspectRatio_enforcePermission();
final int callingUid = Binder.getCallingUid();
final Computer snapshot = snapshotComputer();
snapshot.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */,
"setUserMinAspectRatio");
enforceOwnerRights(snapshot, packageName, callingUid);
final PackageStateInternal packageState = snapshot
.getPackageStateForInstalledAndFiltered(packageName, callingUid, userId);
if (packageState == null) {
return;
}
if (packageState.getUserStateOrDefault(userId).getMinAspectRatio() == aspectRatio) {
return;
}
commitPackageStateMutation(null, packageName, state ->
state.userState(userId).setMinAspectRatio(aspectRatio));
}
@Override
@SuppressWarnings("GuardedBy")
public void setRuntimePermissionsVersion(int version, @UserIdInt int userId) {

View File

@@ -875,7 +875,7 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
int installReason, int uninstallReason,
String harmfulAppWarning, String splashScreenTheme,
long firstInstallTime) {
long firstInstallTime, int aspectRatio) {
modifyUserState(userId)
.setSuspendParams(suspendParams)
.setCeDataInode(ceDataInode)
@@ -894,7 +894,8 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
.setVirtualPreload(virtualPreload)
.setHarmfulAppWarning(harmfulAppWarning)
.setSplashScreenTheme(splashScreenTheme)
.setFirstInstallTimeMillis(firstInstallTime);
.setFirstInstallTimeMillis(firstInstallTime)
.setMinAspectRatio(aspectRatio);
onChanged();
}
@@ -912,7 +913,7 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
? null : otherState.getDisabledComponentsNoCopy().untrackedStorage(),
otherState.getInstallReason(), otherState.getUninstallReason(),
otherState.getHarmfulAppWarning(), otherState.getSplashScreenTheme(),
otherState.getFirstInstallTimeMillis());
otherState.getFirstInstallTimeMillis(), otherState.getMinAspectRatio());
}
WatchedArraySet<String> getEnabledComponents(int userId) {

View File

@@ -351,6 +351,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
private static final String ATTR_VIRTUAL_PRELOAD = "virtual-preload";
private static final String ATTR_HARMFUL_APP_WARNING = "harmful-app-warning";
private static final String ATTR_SPLASH_SCREEN_THEME = "splash-screen-theme";
private static final String ATTR_MIN_ASPECT_RATIO = "min-aspect-ratio";
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_BUILD_FINGERPRINT = "buildFingerprint";
@@ -1122,7 +1123,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/,
null /*splashscreenTheme*/,
0 /*firstInstallTime*/
0 /*firstInstallTime*/,
PackageManager.USER_MIN_ASPECT_RATIO_UNSET
);
}
}
@@ -1776,7 +1778,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/,
null /* splashScreenTheme*/,
0 /*firstInstallTime*/
0 /*firstInstallTime*/,
PackageManager.USER_MIN_ASPECT_RATIO_UNSET
);
}
return;
@@ -1871,6 +1874,9 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
ATTR_SPLASH_SCREEN_THEME);
final long firstInstallTime = parser.getAttributeLongHex(null,
ATTR_FIRST_INSTALL_TIME, 0);
final int minAspectRatio = parser.getAttributeInt(null,
ATTR_MIN_ASPECT_RATIO,
PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
ArraySet<String> enabledComponents = null;
ArraySet<String> disabledComponents = null;
@@ -1947,7 +1953,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
enabledCaller, enabledComponents, disabledComponents, installReason,
uninstallReason, harmfulAppWarning, splashScreenTheme,
firstInstallTime != 0 ? firstInstallTime :
origFirstInstallTimes.getOrDefault(name, 0L));
origFirstInstallTimes.getOrDefault(name, 0L),
minAspectRatio);
mDomainVerificationManager.setLegacyUserState(name, userId, verifState);
} else if (tagName.equals("preferred-activities")) {
@@ -2242,6 +2249,11 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
serializer.attribute(null, ATTR_SPLASH_SCREEN_THEME,
ustate.getSplashScreenTheme());
}
if (ustate.getMinAspectRatio()
!= PackageManager.USER_MIN_ASPECT_RATIO_UNSET) {
serializer.attributeInt(null, ATTR_MIN_ASPECT_RATIO,
ustate.getMinAspectRatio());
}
if (ustate.isSuspended()) {
for (int i = 0; i < ustate.getSuspendParams().size(); i++) {
final String suspendingPackage = ustate.getSuspendParams().keyAt(i);

View File

@@ -377,6 +377,8 @@ public class PackageStateImpl implements PackageState {
private final int mUninstallReason;
@Nullable
private final String mSplashScreenTheme;
@PackageManager.UserMinAspectRatio
private final int mMinAspectRatio;
private final long mFirstInstallTimeMillis;
private UserStateImpl(@NonNull PackageUserState userState) {
@@ -392,6 +394,7 @@ public class PackageStateImpl implements PackageState {
mSharedLibraryOverlayPaths = userState.getSharedLibraryOverlayPaths();
mUninstallReason = userState.getUninstallReason();
mSplashScreenTheme = userState.getSplashScreenTheme();
mMinAspectRatio = userState.getMinAspectRatio();
setBoolean(Booleans.HIDDEN, userState.isHidden());
setBoolean(Booleans.INSTALLED, userState.isInstalled());
setBoolean(Booleans.INSTANT_APP, userState.isInstantApp());
@@ -542,6 +545,11 @@ public class PackageStateImpl implements PackageState {
return mSplashScreenTheme;
}
@DataClass.Generated.Member
public @PackageManager.UserMinAspectRatio int getMinAspectRatio() {
return mMinAspectRatio;
}
@DataClass.Generated.Member
public long getFirstInstallTimeMillis() {
return mFirstInstallTimeMillis;
@@ -554,10 +562,10 @@ public class PackageStateImpl implements PackageState {
}
@DataClass.Generated(
time = 1671671043891L,
time = 1687938966108L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate final long mFirstInstallTimeMillis\npublic static com.android.server.pm.pkg.PackageUserState copy(com.android.server.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate final @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate final long mFirstInstallTimeMillis\npublic static com.android.server.pm.pkg.PackageUserState copy(com.android.server.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@Deprecated
private void __metadata() {}

View File

@@ -217,4 +217,12 @@ public interface PackageUserState {
*/
@Nullable
String getSplashScreenTheme();
/**
* @return the min aspect ratio setting of the package which by default is unset
* unless it has been set by the user
* @hide
*/
@PackageManager.UserMinAspectRatio
int getMinAspectRatio();
}

View File

@@ -135,6 +135,11 @@ class PackageUserStateDefault implements PackageUserStateInternal {
return null;
}
@Override
public @PackageManager.UserMinAspectRatio int getMinAspectRatio() {
return PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
}
@Override
public long getFirstInstallTimeMillis() {
return 0;

File diff suppressed because one or more lines are too long

View File

@@ -439,6 +439,16 @@ public class PackageStateMutator {
}
return null;
}
@NonNull
@Override
public PackageUserStateWrite setMinAspectRatio(
@PackageManager.UserMinAspectRatio int aspectRatio) {
if (mUserState != null) {
mUserState.setMinAspectRatio(aspectRatio);
}
return this;
}
}
}
}

View File

@@ -22,6 +22,7 @@ import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.overlay.OverlayPaths;
import com.android.server.pm.pkg.PackageUserStateImpl;
import com.android.server.pm.pkg.SuspendParams;
public interface PackageUserStateWrite {
@@ -68,4 +69,8 @@ public interface PackageUserStateWrite {
@NonNull
PackageUserStateWrite setComponentLabelIcon(@NonNull ComponentName componentName,
@Nullable String nonLocalizedLabel, @Nullable Integer icon);
/** @see PackageUserStateImpl#setMinAspectRatio(int) */
@NonNull
PackageUserStateWrite setMinAspectRatio(@PackageManager.UserMinAspectRatio int aspectRatio);
}

View File

@@ -137,6 +137,14 @@ public class CrossUserPackageVisibilityTests {
mInstrumentation.getContext().getPackageName(), crossUserId));
}
@Test
public void testGetUserMinAspectRatio_withCrossUserId() {
final int crossUserId = UserHandle.myUserId() + 1;
assertThrows(SecurityException.class,
() -> mIPackageManager.getUserMinAspectRatio(
mInstrumentation.getContext().getPackageName(), crossUserId));
}
@Test
public void testIsPackageSignedByKeySet_cannotDetectCrossUserPkg() throws Exception {
final KeySet keySet = mIPackageManager.getSigningKeySet(mContext.getPackageName());

View File

@@ -20,6 +20,7 @@ import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static java.lang.reflect.Modifier.isFinal;
@@ -138,7 +139,7 @@ public class PackageManagerServiceTest {
.setSecondaryCpuAbiString("secondaryCpuAbiString")
.setCpuAbiOverrideString("cpuAbiOverrideString")
.build();
pri.populateUsers(new int[] {
pri.populateUsers(new int[]{
1, 2, 3, 4, 5
}, setting);
Assert.assertNotNull(pri.mBroadcastUsers);
@@ -150,7 +151,7 @@ public class PackageManagerServiceTest {
pri.mBroadcastUsers = null;
final int EXCLUDED_USER_ID = 4;
setting.setInstantApp(true, EXCLUDED_USER_ID);
pri.populateUsers(new int[] {
pri.populateUsers(new int[]{
1, 2, 3, EXCLUDED_USER_ID, 5
}, setting);
Assert.assertNotNull(pri.mBroadcastUsers);
@@ -164,8 +165,8 @@ public class PackageManagerServiceTest {
@Test
public void testPartitions() {
String[] partitions = { "system", "vendor", "odm", "oem", "product", "system_ext" };
String[] appdir = { "app", "priv-app" };
String[] partitions = {"system", "vendor", "odm", "oem", "product", "system_ext"};
String[] appdir = {"app", "priv-app"};
for (int i = 0; i < partitions.length; i++) {
final ScanPartition scanPartition =
PackageManagerService.SYSTEM_PARTITIONS.get(i);
@@ -425,10 +426,10 @@ public class PackageManagerServiceTest {
private String displayName(Method m) {
String r = m.getName();
String p = Arrays.toString(m.getGenericParameterTypes())
.replaceAll("([a-zA-Z0-9]+\\.)+", "")
.replace("class ", "")
.replaceAll("^\\[", "(")
.replaceAll("\\]$", ")");
.replaceAll("([a-zA-Z0-9]+\\.)+", "")
.replace("class ", "")
.replaceAll("^\\[", "(")
.replaceAll("\\]$", ")");
return r + p;
}
@@ -612,4 +613,22 @@ public class PackageManagerServiceTest {
runShellCommand("pm uninstall " + TEST_PKG_NAME);
}
}
@Test
public void testSetUserMinAspectRatio_samePackage_succeeds() throws Exception {
mIPackageManager.setUserMinAspectRatio(PACKAGE_NAME, UserHandle.myUserId(),
PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
// Invoking setUserMinAspectRatio on the same package shouldn't get any exception.
}
@Test
public void testSetUserMinAspectRatio_differentPackage_fails() {
final File testApk = new File(TEST_DATA_PATH, TEST_APP_APK);
runShellCommand("pm install " + testApk);
assertThrows(SecurityException.class, () -> {
mIPackageManager.setUserMinAspectRatio(TEST_PKG_NAME, UserHandle.myUserId(),
PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
});
runShellCommand("pm uninstall " + TEST_PKG_NAME);
}
}

View File

@@ -971,7 +971,7 @@ public class PackageManagerSettingsTests {
origPkgSetting01.setUserState(0, 100, 1, true, false, false, false, 0, null, false,
false, "lastDisabledCaller", new ArraySet<>(new String[]{"enabledComponent1"}),
new ArraySet<>(new String[]{"disabledComponent1"}), 0, 0, "harmfulAppWarning",
"splashScreenTheme", 1000L);
"splashScreenTheme", 1000L, PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
final PersistableBundle appExtras1 = createPersistableBundle(
PACKAGE_NAME_1, 1L, 0.01, true, "appString1");
final PersistableBundle launcherExtras1 = createPersistableBundle(
@@ -1638,7 +1638,8 @@ public class PackageManagerSettingsTests {
: oldUserState.getSharedLibraryOverlayPaths() == null)
&& userState.getSplashScreenTheme().equals(
oldUserState.getSplashScreenTheme())
&& userState.getUninstallReason() == oldUserState.getUninstallReason();
&& userState.getUninstallReason() == oldUserState.getUninstallReason()
&& userState.getMinAspectRatio() == oldUserState.getMinAspectRatio();
}
private SharedUserSetting createSharedUserSetting(Settings settings, String userName,