diff options
author | Jeff Sharkey <jsharkey@google.com> | 2017-06-13 00:24:59 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2017-06-13 00:24:59 +0000 |
commit | dc292b6003c80aa954bd82ca06d1b4eec271144e (patch) | |
tree | f7f6f40893bbea008a7b3ee42e798ce02a3a7528 | |
parent | b8b6f00b759e467f083831470870ad0848fdcbe5 (diff) | |
parent | de624f3fbbce27ed614b954988f1f6e39d92e6ed (diff) |
Merge "Active camera apps can defy reserved cache space." into oc-dr1-dev am: f7dc56ecc9
am: de624f3fbb
Change-Id: I0a125a6c00220ba280dca4e0d8ece88202a68afe
10 files changed, 107 insertions, 44 deletions
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 9813cb1631ce..ad989dee7b55 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -1473,7 +1473,7 @@ public final class Pm { ClearDataObserver obs = new ClearDataObserver(); try { mPm.freeStorageAndNotify(volumeUuid, sizeVal, - StorageManager.FLAG_ALLOCATE_DEFY_RESERVED, obs); + StorageManager.FLAG_ALLOCATE_DEFY_ALL_RESERVED, obs); synchronized (obs) { while (!obs.finished) { try { diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index e672ada3cbb4..b331d84010d0 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1947,4 +1947,13 @@ public class AppOpsManager { public void finishOp(int op) { finishOp(op, Process.myUid(), mContext.getOpPackageName()); } + + /** @hide */ + public boolean isOperationActive(int code, int uid, String packageName) { + try { + return mService.isOperationActive(code, uid, packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl index 92f7f319a14b..50855bb349d9 100644 --- a/core/java/android/os/storage/IStorageManager.aidl +++ b/core/java/android/os/storage/IStorageManager.aidl @@ -293,7 +293,7 @@ interface IStorageManager { ParcelFileDescriptor openProxyFileDescriptor(int mountPointId, int fileId, int mode) = 74; long getCacheQuotaBytes(String volumeUuid, int uid) = 75; long getCacheSizeBytes(String volumeUuid, int uid) = 76; - long getAllocatableBytes(String volumeUuid, int flags) = 77; - void allocateBytes(String volumeUuid, long bytes, int flags) = 78; + long getAllocatableBytes(String volumeUuid, int flags, String callingPackage) = 77; + void allocateBytes(String volumeUuid, long bytes, int flags, String callingPackage) = 78; void secdiscard(in String path) = 79; } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 504673529238..ca7c73903481 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1174,7 +1174,7 @@ public class StorageManager { * * @hide */ - public long getStorageCacheBytes(File path) { + public long getStorageCacheBytes(File path, @AllocateFlags int flags) { final long cachePercent = Settings.Global.getInt(mResolver, Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE, DEFAULT_CACHE_PERCENTAGE); final long cacheBytes = (path.getTotalSpace() * cachePercent) / 100; @@ -1182,7 +1182,16 @@ public class StorageManager { final long maxCacheBytes = Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES, DEFAULT_CACHE_MAX_BYTES); - return Math.min(cacheBytes, maxCacheBytes); + final long result = Math.min(cacheBytes, maxCacheBytes); + if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) { + return 0; + } else if ((flags & StorageManager.FLAG_ALLOCATE_DEFY_ALL_RESERVED) != 0) { + return 0; + } else if ((flags & StorageManager.FLAG_ALLOCATE_DEFY_HALF_RESERVED) != 0) { + return result / 2; + } else { + return result; + } } /** @@ -1628,17 +1637,26 @@ public class StorageManager { public static final int FLAG_ALLOCATE_AGGRESSIVE = 1 << 0; /** - * Flag indicating that a disk space allocation request should defy any - * reserved disk space. + * Flag indicating that a disk space allocation request should be allowed to + * clear up to all reserved disk space. + * + * @hide + */ + public static final int FLAG_ALLOCATE_DEFY_ALL_RESERVED = 1 << 1; + + /** + * Flag indicating that a disk space allocation request should be allowed to + * clear up to half of all reserved disk space. * * @hide */ - public static final int FLAG_ALLOCATE_DEFY_RESERVED = 1 << 1; + public static final int FLAG_ALLOCATE_DEFY_HALF_RESERVED = 1 << 2; /** @hide */ @IntDef(flag = true, value = { FLAG_ALLOCATE_AGGRESSIVE, - FLAG_ALLOCATE_DEFY_RESERVED, + FLAG_ALLOCATE_DEFY_ALL_RESERVED, + FLAG_ALLOCATE_DEFY_HALF_RESERVED, }) @Retention(RetentionPolicy.SOURCE) public @interface AllocateFlags {} @@ -1688,7 +1706,8 @@ public class StorageManager { public long getAllocatableBytes(@NonNull UUID storageUuid, @RequiresPermission @AllocateFlags int flags) throws IOException { try { - return mStorageManager.getAllocatableBytes(convert(storageUuid), flags); + return mStorageManager.getAllocatableBytes(convert(storageUuid), flags, + mContext.getOpPackageName()); } catch (ParcelableException e) { e.maybeRethrow(IOException.class); throw new RuntimeException(e); @@ -1738,7 +1757,8 @@ public class StorageManager { public void allocateBytes(@NonNull UUID storageUuid, @BytesLong long bytes, @RequiresPermission @AllocateFlags int flags) throws IOException { try { - mStorageManager.allocateBytes(convert(storageUuid), bytes, flags); + mStorageManager.allocateBytes(convert(storageUuid), bytes, flags, + mContext.getOpPackageName()); } catch (ParcelableException e) { e.maybeRethrow(IOException.class); } catch (RemoteException e) { diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 35d4ba81bd69..7a119b4351c0 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -48,4 +48,6 @@ interface IAppOpsService { void setUserRestrictions(in Bundle restrictions, IBinder token, int userHandle); void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle, in String[] exceptionPackages); void removeUser(int userHandle); + + boolean isOperationActive(int code, int uid, String packageName); } diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index 1a5ec61a1510..c8e6e2efb240 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -2445,6 +2445,28 @@ public class AppOpsService extends IAppOpsService.Stub { } } + @Override + public boolean isOperationActive(int code, int uid, String packageName) { + verifyIncomingUid(uid); + verifyIncomingOp(code); + String resolvedPackageName = resolvePackageName(uid, packageName); + if (resolvedPackageName == null) { + return false; + } + synchronized (this) { + for (int i = mClients.size() - 1; i >= 0; i--) { + final ClientState client = mClients.valueAt(i); + if (client.mStartedOps == null) continue; + + for (int j = client.mStartedOps.size() - 1; j >= 0; j--) { + final Op op = client.mStartedOps.get(j); + if (op.op == code && op.uid == uid) return true; + } + } + } + return false; + } + private void removeUidsForUserLocked(int userHandle) { for (int i = mUidStates.size() - 1; i >= 0; --i) { final int uid = mUidStates.keyAt(i); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 35b452a12e64..f718e803a973 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -90,14 +90,12 @@ import android.util.AtomicFile; import android.util.Log; import android.util.Pair; import android.util.Slog; -import android.util.SparseArray; import android.util.TimeUtils; import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IMediaContainerService; import com.android.internal.os.AppFuseMount; -import com.android.internal.os.FuseAppLoop; import com.android.internal.os.FuseUnavailableMountException; import com.android.internal.os.SomeArgs; import com.android.internal.os.Zygote; @@ -143,7 +141,6 @@ import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; -import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -3291,20 +3288,41 @@ class StorageManagerService extends IStorageManager.Stub } } - @Override - public long getAllocatableBytes(String volumeUuid, int flags) { - final StorageManager storage = mContext.getSystemService(StorageManager.class); - final StorageStatsManager stats = mContext.getSystemService(StorageStatsManager.class); - - // Apps can't defy reserved space - flags &= ~StorageManager.FLAG_ALLOCATE_DEFY_RESERVED; - - final boolean aggressive = (flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0; - if (aggressive) { + private int adjustAllocateFlags(int flags, int callingUid, String callingPackage) { + // Require permission to allocate aggressively + if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ALLOCATE_AGGRESSIVE, TAG); } + // Apps normally can't directly defy reserved space + flags &= ~StorageManager.FLAG_ALLOCATE_DEFY_ALL_RESERVED; + flags &= ~StorageManager.FLAG_ALLOCATE_DEFY_HALF_RESERVED; + + // However, if app is actively using the camera, then we're willing to + // clear up to half of the reserved cache space, since the user might be + // trying to capture an important memory. + final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); + final long token = Binder.clearCallingIdentity(); + try { + if (appOps.isOperationActive(AppOpsManager.OP_CAMERA, callingUid, callingPackage)) { + Slog.d(TAG, "UID " + callingUid + " is actively using camera;" + + " letting them defy reserved cached data"); + flags |= StorageManager.FLAG_ALLOCATE_DEFY_HALF_RESERVED; + } + } finally { + Binder.restoreCallingIdentity(token); + } + + return flags; + } + + @Override + public long getAllocatableBytes(String volumeUuid, int flags, String callingPackage) { + flags = adjustAllocateFlags(flags, Binder.getCallingUid(), callingPackage); + + final StorageManager storage = mContext.getSystemService(StorageManager.class); + final StorageStatsManager stats = mContext.getSystemService(StorageStatsManager.class); final long token = Binder.clearCallingIdentity(); try { // In general, apps can allocate as much space as they want, except @@ -3319,18 +3337,18 @@ class StorageManagerService extends IStorageManager.Stub if (stats.isQuotaSupported(volumeUuid)) { final long cacheTotal = stats.getCacheBytes(volumeUuid); - final long cacheReserved = storage.getStorageCacheBytes(path); + final long cacheReserved = storage.getStorageCacheBytes(path, flags); final long cacheClearable = Math.max(0, cacheTotal - cacheReserved); - if (aggressive) { - return Math.max(0, (usable + cacheTotal) - fullReserved); + if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) { + return Math.max(0, (usable + cacheClearable) - fullReserved); } else { return Math.max(0, (usable + cacheClearable) - lowReserved); } } else { // When we don't have fast quota information, we ignore cached // data and only consider unused bytes. - if (aggressive) { + if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) { return Math.max(0, usable - fullReserved); } else { return Math.max(0, usable - lowReserved); @@ -3344,20 +3362,16 @@ class StorageManagerService extends IStorageManager.Stub } @Override - public void allocateBytes(String volumeUuid, long bytes, int flags) { - final StorageManager storage = mContext.getSystemService(StorageManager.class); + public void allocateBytes(String volumeUuid, long bytes, int flags, String callingPackage) { + flags = adjustAllocateFlags(flags, Binder.getCallingUid(), callingPackage); - // Apps can't defy reserved space - flags &= ~StorageManager.FLAG_ALLOCATE_DEFY_RESERVED; - - // This method call will enforce FLAG_ALLOCATE_AGGRESSIVE permissions so - // we don't have to enforce them locally - final long allocatableBytes = getAllocatableBytes(volumeUuid, flags); + final long allocatableBytes = getAllocatableBytes(volumeUuid, flags, callingPackage); if (bytes > allocatableBytes) { throw new ParcelableException(new IOException("Failed to allocate " + bytes + " because only " + allocatableBytes + " allocatable")); } + final StorageManager storage = mContext.getSystemService(StorageManager.class); final long token = Binder.clearCallingIdentity(); try { // Free up enough disk space to satisfy both the requested allocation diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 48b9c7de650b..9c04801c056b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -4258,10 +4258,7 @@ public class PackageManagerService extends IPackageManager.Stub volumeUuid); final boolean aggressive = (storageFlags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0; - final boolean defyReserved = (storageFlags - & StorageManager.FLAG_ALLOCATE_DEFY_RESERVED) != 0; - final long reservedBytes = (aggressive || defyReserved) ? 0 - : storage.getStorageCacheBytes(file); + final long reservedBytes = storage.getStorageCacheBytes(file, storageFlags); // 1. Pre-flight to determine if we have any chance to succeed // 2. Consider preloaded data (after 1w honeymoon, unless aggressive) diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockStorageManager.java b/services/tests/servicestests/src/com/android/server/locksettings/MockStorageManager.java index 89e18b4c2f41..40e114bfaef2 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/MockStorageManager.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/MockStorageManager.java @@ -491,12 +491,12 @@ public class MockStorageManager implements IStorageManager { } @Override - public long getAllocatableBytes(String path, int flags) { + public long getAllocatableBytes(String path, int flags, String callingPackage) { throw new UnsupportedOperationException(); } @Override - public void allocateBytes(String path, long bytes, int flags) { + public void allocateBytes(String path, long bytes, int flags, String callingPackage) { throw new UnsupportedOperationException(); } @@ -504,5 +504,4 @@ public class MockStorageManager implements IStorageManager { public void secdiscard(String path) throws RemoteException { throw new UnsupportedOperationException(); } - } diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java index 562443f53546..9f4fb85f64c4 100644 --- a/services/usage/java/com/android/server/usage/StorageStatsService.java +++ b/services/usage/java/com/android/server/usage/StorageStatsService.java @@ -197,7 +197,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub { // logic should be kept in sync with getAllocatableBytes(). if (isQuotaSupported(volumeUuid, callingPackage)) { final long cacheTotal = getCacheBytes(volumeUuid, callingPackage); - final long cacheReserved = mStorage.getStorageCacheBytes(path); + final long cacheReserved = mStorage.getStorageCacheBytes(path, 0); final long cacheClearable = Math.max(0, cacheTotal - cacheReserved); return path.getUsableSpace() + cacheClearable; |