Merge changes from topic "preserve_legacy"
* changes: Enforce the preserveLegacyExternalStorage manifest attribute Support preserveLegacyExternalStorage manifest attribute Introduce preserveLegacyExternalStorage manifest attribute
This commit is contained in:
@@ -1082,6 +1082,7 @@ package android {
|
||||
field public static final int preferenceScreenStyle = 16842891; // 0x101008b
|
||||
field public static final int preferenceStyle = 16842894; // 0x101008e
|
||||
field public static final int presentationTheme = 16843712; // 0x10103c0
|
||||
field public static final int preserveLegacyExternalStorage = 16844312; // 0x1010618
|
||||
field public static final int previewImage = 16843482; // 0x10102da
|
||||
field public static final int primaryContentAlpha = 16844114; // 0x1010552
|
||||
field public static final int priority = 16842780; // 0x101001c
|
||||
|
||||
@@ -397,6 +397,8 @@ public interface AndroidPackage extends Parcelable {
|
||||
|
||||
boolean hasComponentClassName(String className);
|
||||
|
||||
boolean hasPreserveLegacyExternalStorage();
|
||||
|
||||
// App Info
|
||||
|
||||
boolean hasRequestedLegacyExternalStorage();
|
||||
|
||||
@@ -2101,6 +2101,10 @@ public class ApkParseUtils {
|
||||
parsingPackage.setAllowNativeHeapPointerTagging(sa.getBoolean(
|
||||
R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, true));
|
||||
|
||||
parsingPackage.setPreserveLegacyExternalStorage(sa.getBoolean(
|
||||
R.styleable.AndroidManifestApplication_preserveLegacyExternalStorage,
|
||||
false));
|
||||
|
||||
parsingPackage
|
||||
.setMaxAspectRatio(
|
||||
sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0))
|
||||
|
||||
@@ -289,6 +289,7 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
|
||||
private String[] usesLibraryFiles;
|
||||
private List<SharedLibraryInfo> usesLibraryInfos;
|
||||
private String zygotePreloadName;
|
||||
private boolean preserveLegacyExternalStorage;
|
||||
|
||||
@VisibleForTesting
|
||||
public PackageImpl(
|
||||
@@ -1518,6 +1519,12 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageImpl setPreserveLegacyExternalStorage(boolean preserveLegacyExternalStorage) {
|
||||
this.preserveLegacyExternalStorage = preserveLegacyExternalStorage;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageImpl setUsesNonSdkApi(boolean usesNonSdkApi) {
|
||||
this.privateFlags = usesNonSdkApi
|
||||
@@ -2999,6 +3006,11 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
|
||||
return queriesProviders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPreserveLegacyExternalStorage() {
|
||||
return preserveLegacyExternalStorage;
|
||||
}
|
||||
|
||||
private static void internStringArrayList(List<String> list) {
|
||||
if (list != null) {
|
||||
final int N = list.size();
|
||||
|
||||
@@ -192,6 +192,8 @@ public interface ParsingPackage extends AndroidPackage {
|
||||
ParsingPackage setRequestLegacyExternalStorage(boolean requestLegacyExternalStorage);
|
||||
|
||||
ParsingPackage setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging);
|
||||
|
||||
ParsingPackage setPreserveLegacyExternalStorage(boolean preserveLegacyExternalStorage);
|
||||
|
||||
ParsingPackage setRestoreAnyVersion(boolean restoreAnyVersion);
|
||||
|
||||
|
||||
@@ -118,4 +118,10 @@ public abstract class StorageManagerInternal {
|
||||
* @param userId the userId for which to reset storage
|
||||
*/
|
||||
public abstract void resetUser(int userId);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the immediate last installed version of an app with {@code uid} had
|
||||
* legacy storage, {@code false} otherwise.
|
||||
*/
|
||||
public abstract boolean hasLegacyExternalStorage(int uid);
|
||||
}
|
||||
|
||||
@@ -1770,6 +1770,21 @@
|
||||
-->
|
||||
<attr name="requestLegacyExternalStorage" format="boolean" />
|
||||
|
||||
<!-- If {@code true} this app would like to preserve the legacy storage
|
||||
model from a previously installed version. Note that this may not always be
|
||||
respected due to policy or backwards compatibility reasons.
|
||||
|
||||
<p>This has no effect on the first install of an app on a device.
|
||||
For an updating app, setting this to {@code true} will preserve the legacy behaviour
|
||||
configured by the {@code requestLegacyExternalStorage} flag. If on an update, this
|
||||
flag is set to {@code false} then the legacy access is not preserved, such an app can
|
||||
only have legacy access with the {@code requestLegacyExternalStorage} flag.
|
||||
<p>
|
||||
|
||||
The default value is {@code false}.
|
||||
-->
|
||||
<attr name="preserveLegacyExternalStorage" format="boolean" />
|
||||
|
||||
<!-- If {@code true} this app declares that it should be visible to all other apps on
|
||||
device, regardless of what they declare via the {@code queries} tags in their
|
||||
manifest.
|
||||
|
||||
@@ -3018,6 +3018,7 @@
|
||||
<!-- @hide @SystemApi -->
|
||||
<public name="isAutofillInlineSuggestionTheme" />
|
||||
<public name="allowNativeHeapPointerTagging" />
|
||||
<public name="preserveLegacyExternalStorage" />
|
||||
</public-group>
|
||||
|
||||
<public-group type="drawable" first-id="0x010800b5">
|
||||
|
||||
@@ -28,6 +28,10 @@ import static android.app.AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE;
|
||||
import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE;
|
||||
import static android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES;
|
||||
import static android.app.AppOpsManager.OP_WRITE_EXTERNAL_STORAGE;
|
||||
import static android.content.pm.PackageManager.MATCH_ANY_USER;
|
||||
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
|
||||
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
|
||||
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
|
||||
import static android.os.storage.OnObbStateChangeListener.ERROR_ALREADY_MOUNTED;
|
||||
@@ -136,6 +140,7 @@ import android.util.Xml;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.app.IAppOpsCallback;
|
||||
import com.android.internal.app.IAppOpsService;
|
||||
import com.android.internal.content.PackageMonitor;
|
||||
import com.android.internal.os.AppFuseMount;
|
||||
import com.android.internal.os.BackgroundThread;
|
||||
import com.android.internal.os.FuseUnavailableMountException;
|
||||
@@ -149,6 +154,7 @@ import com.android.internal.util.HexDump;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.server.SystemService.TargetUser;
|
||||
import com.android.server.pm.Installer;
|
||||
import com.android.server.storage.AppFuseBridge;
|
||||
import com.android.server.storage.StorageSessionController;
|
||||
@@ -184,6 +190,7 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -270,6 +277,11 @@ class StorageManagerService extends IStorageManager.Stub
|
||||
public void onStopUser(int userHandle) {
|
||||
mStorageManagerService.onStopUser(userHandle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartUser(TargetUser user) {
|
||||
mStorageManagerService.snapshotAndMonitorLegacyStorageAppOp(user.getUserHandle());
|
||||
}
|
||||
}
|
||||
|
||||
private static final boolean DEBUG_EVENTS = false;
|
||||
@@ -578,6 +590,12 @@ class StorageManagerService extends IStorageManager.Stub
|
||||
|
||||
private final boolean mIsFuseEnabled;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private final Set<Integer> mUidsWithLegacyExternalStorage = new ArraySet<>();
|
||||
// Not guarded by lock, always used on the ActivityManager thread
|
||||
private final Map<Integer, PackageMonitor> mPackageMonitorsForUser = new ArrayMap<>();
|
||||
|
||||
|
||||
class ObbState implements IBinder.DeathRecipient {
|
||||
public ObbState(String rawPath, String canonicalPath, int callingUid,
|
||||
IObbActionListener token, int nonce, String volId) {
|
||||
@@ -1145,6 +1163,10 @@ class StorageManagerService extends IStorageManager.Stub
|
||||
} catch (Exception e) {
|
||||
Slog.wtf(TAG, e);
|
||||
}
|
||||
PackageMonitor monitor = mPackageMonitorsForUser.remove(userId);
|
||||
if (monitor != null) {
|
||||
monitor.unregister();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean supportsBlockCheckpoint() throws RemoteException {
|
||||
@@ -1836,6 +1858,49 @@ class StorageManagerService extends IStorageManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
private void updateLegacyStorageApps(String packageName, int uid, boolean hasLegacy) {
|
||||
synchronized (mLock) {
|
||||
if (hasLegacy) {
|
||||
Slog.v(TAG, "Package " + packageName + " has legacy storage");
|
||||
mUidsWithLegacyExternalStorage.add(uid);
|
||||
} else {
|
||||
// TODO(b/149391976): Handle shared user id. Check if there's any other
|
||||
// installed app with legacy external storage before removing
|
||||
Slog.v(TAG, "Package " + packageName + " does not have legacy storage");
|
||||
mUidsWithLegacyExternalStorage.remove(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void snapshotAndMonitorLegacyStorageAppOp(UserHandle user) {
|
||||
int userId = user.getIdentifier();
|
||||
|
||||
// TODO(b/149391976): Use mIAppOpsService.getPackagesForOps instead of iterating below
|
||||
// It should improve performance but the AppOps method doesn't return any app here :(
|
||||
// This operation currently takes about ~20ms on a freshly flashed device
|
||||
for (ApplicationInfo ai : mPmInternal.getInstalledApplications(MATCH_DIRECT_BOOT_AWARE
|
||||
| MATCH_DIRECT_BOOT_UNAWARE | MATCH_UNINSTALLED_PACKAGES | MATCH_ANY_USER,
|
||||
userId, Process.myUid())) {
|
||||
try {
|
||||
boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE, ai.uid,
|
||||
ai.packageName) == MODE_ALLOWED;
|
||||
updateLegacyStorageApps(ai.packageName, ai.uid, hasLegacy);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to check legacy op for package " + ai.packageName, e);
|
||||
}
|
||||
}
|
||||
|
||||
PackageMonitor monitor = new PackageMonitor() {
|
||||
@Override
|
||||
public void onPackageRemoved(String packageName, int uid) {
|
||||
updateLegacyStorageApps(packageName, uid, false);
|
||||
}
|
||||
};
|
||||
// TODO(b/149391976): Use different handler?
|
||||
monitor.register(mContext, user, true, mHandler);
|
||||
mPackageMonitorsForUser.put(userId, monitor);
|
||||
}
|
||||
|
||||
private static long getLastAccessTime(AppOpsManager manager,
|
||||
int uid, String packageName, int[] ops) {
|
||||
long maxTime = 0;
|
||||
@@ -4337,6 +4402,13 @@ class StorageManagerService extends IStorageManager.Stub
|
||||
mHandler.obtainMessage(H_RESET).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLegacyExternalStorage(int uid) {
|
||||
synchronized (mLock) {
|
||||
return mUidsWithLegacyExternalStorage.contains(uid);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasExternalStorage(int uid, String packageName) {
|
||||
// No need to check for system uid. This avoids a deadlock between
|
||||
// PackageManagerService and AppOpsService.
|
||||
@@ -4382,8 +4454,11 @@ class StorageManagerService extends IStorageManager.Stub
|
||||
// volumes, USB OTGs that are rarely mounted. The app will get the
|
||||
// external_storage gid on next organic restart.
|
||||
killAppForOpChange(code, uid, packageName);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
case OP_LEGACY_STORAGE:
|
||||
updateLegacyStorageApps(packageName, uid, mode == MODE_ALLOWED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,14 +35,17 @@ import android.compat.annotation.EnabledAfter;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.UserHandle;
|
||||
import android.os.storage.StorageManagerInternal;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.compat.IPlatformCompat;
|
||||
import com.android.server.LocalServices;
|
||||
|
||||
/**
|
||||
* The behavior of soft restricted permissions is different for each permission. This class collects
|
||||
@@ -107,11 +110,16 @@ public abstract class SoftRestrictedPermissionPolicy {
|
||||
final boolean isWhiteListed;
|
||||
boolean shouldApplyRestriction;
|
||||
final boolean hasRequestedLegacyExternalStorage;
|
||||
final boolean shouldPreserveLegacyExternalStorage;
|
||||
final boolean hasWriteMediaStorageGrantedForUid;
|
||||
final boolean isScopedStorageEnabled;
|
||||
|
||||
if (appInfo != null) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
PackageManagerInternal pmInternal =
|
||||
LocalServices.getService(PackageManagerInternal.class);
|
||||
StorageManagerInternal smInternal =
|
||||
LocalServices.getService(StorageManagerInternal.class);
|
||||
int flags = pm.getPermissionFlags(permission, appInfo.packageName, user);
|
||||
isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
|
||||
hasRequestedLegacyExternalStorage = hasUidRequestedLegacyExternalStorage(
|
||||
@@ -123,12 +131,16 @@ public abstract class SoftRestrictedPermissionPolicy {
|
||||
isScopedStorageEnabled =
|
||||
isChangeEnabledForUid(context, appInfo, user, ENABLE_SCOPED_STORAGE)
|
||||
|| isScopedStorageRequired;
|
||||
shouldPreserveLegacyExternalStorage = pmInternal.getPackage(
|
||||
appInfo.packageName).hasPreserveLegacyExternalStorage()
|
||||
&& smInternal.hasLegacyExternalStorage(appInfo.uid);
|
||||
shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0
|
||||
|| isScopedStorageRequired;
|
||||
|| (isScopedStorageRequired && !shouldPreserveLegacyExternalStorage);
|
||||
} else {
|
||||
isWhiteListed = false;
|
||||
shouldApplyRestriction = false;
|
||||
hasRequestedLegacyExternalStorage = false;
|
||||
shouldPreserveLegacyExternalStorage = false;
|
||||
hasWriteMediaStorageGrantedForUid = false;
|
||||
isScopedStorageEnabled = false;
|
||||
}
|
||||
@@ -150,7 +162,8 @@ public abstract class SoftRestrictedPermissionPolicy {
|
||||
public boolean mayAllowExtraAppOp() {
|
||||
return !shouldApplyRestriction
|
||||
&& (hasRequestedLegacyExternalStorage
|
||||
|| hasWriteMediaStorageGrantedForUid);
|
||||
|| hasWriteMediaStorageGrantedForUid
|
||||
|| shouldPreserveLegacyExternalStorage);
|
||||
}
|
||||
@Override
|
||||
public boolean mayDenyExtraAppOpIfGranted() {
|
||||
|
||||
Reference in New Issue
Block a user