summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2017-06-09 10:01:20 -0600
committerJeff Sharkey <jsharkey@android.com>2017-06-12 14:27:23 -0600
commit35e46d297255363a20ccde62af3c58c4ce3c13c5 (patch)
tree6a082247418b5cc02d46e4a351a4d3f930ce779c
parent9620595965bc7ffb74518eee19bbd63702c57121 (diff)
Active camera apps can defy reserved cache space.
We normally prevent apps from allocating into the "reserved" cache space, but this change makes an exception for an active camera app, since the user is probably trying to capture an important memory. This change only lets the active camera app clear up to half of the reserved space, since we don't want to completely destroy the experience of all other apps. Test: manual app before/during/after active camera session Bug: 38267830 Change-Id: Ie9e63884fb2638ca881e10b894629eea84601648
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java2
-rw-r--r--core/java/android/app/AppOpsManager.java9
-rw-r--r--core/java/android/os/storage/IStorageManager.aidl4
-rw-r--r--core/java/android/os/storage/StorageManager.java36
-rw-r--r--core/java/com/android/internal/app/IAppOpsService.aidl2
-rw-r--r--services/core/java/com/android/server/AppOpsService.java22
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java64
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/MockStorageManager.java5
-rw-r--r--services/usage/java/com/android/server/usage/StorageStatsService.java2
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 d71573f7ca50..c4193f647c32 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;