diff --git a/core/api/current.txt b/core/api/current.txt index b83e642400de0..3fcff2b3894c1 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -32273,6 +32273,7 @@ package android.os.storage { method @WorkerThread public long getAllocatableBytes(@NonNull java.util.UUID) throws java.io.IOException; method @WorkerThread public long getCacheQuotaBytes(@NonNull java.util.UUID) throws java.io.IOException; method @WorkerThread public long getCacheSizeBytes(@NonNull java.util.UUID) throws java.io.IOException; + method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE) public android.app.PendingIntent getManageSpaceActivityIntent(@NonNull String, int); method public String getMountedObbPath(String); method @NonNull public android.os.storage.StorageVolume getPrimaryStorageVolume(); method @NonNull public java.util.List getRecentStorageVolumes(); diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl index 4669b208b163a..815990a6fd187 100644 --- a/core/java/android/os/storage/IStorageManager.aidl +++ b/core/java/android/os/storage/IStorageManager.aidl @@ -28,6 +28,8 @@ import android.os.storage.StorageVolume; import android.os.storage.VolumeInfo; import android.os.storage.VolumeRecord; import com.android.internal.os.AppFuseMount; +import android.app.PendingIntent; + /** * WARNING! Update IMountService.h and IMountService.cpp if you change this @@ -196,4 +198,5 @@ interface IStorageManager { void clearUserKeyAuth(int userId, int serialNumber, in byte[] token, in byte[] secret) = 88; void fixupAppDir(in String path) = 89; void disableAppDataIsolation(in String pkgName, int pid, int userId) = 90; + PendingIntent getManageSpaceActivityIntent(in String packageName, int requestCode) = 91; } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 3a5426c60b24b..6c1c58f9e1d96 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -49,6 +49,7 @@ import android.app.Activity; import android.app.ActivityThread; import android.app.AppGlobals; import android.app.AppOpsManager; +import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentResolver; import android.content.Context; @@ -701,6 +702,33 @@ public class StorageManager { } } + /** + * Returns a {@link PendingIntent} that can be used by Apps with + * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} permission + * to launch the manageSpaceActivity for any App that implements it, irrespective of its + * exported status. + *

+ * Caller has the responsibility of supplying a valid packageName which has + * manageSpaceActivity implemented. + * + * @param packageName package name for the App for which manageSpaceActivity is to be launched + * @param requestCode for launching the activity + * @return PendingIntent to launch the manageSpaceActivity if successful, null if the + * packageName doesn't have a manageSpaceActivity. + * @throws IllegalArgumentException an invalid packageName is supplied. + */ + @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE) + @Nullable + public PendingIntent getManageSpaceActivityIntent( + @NonNull String packageName, int requestCode) { + try { + return mStorageManager.getManageSpaceActivityIntent(packageName, + requestCode); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + private ObbInfo getObbInfo(String canonicalPath) { try { final ObbInfo obbInfo = ObbScanner.getObbInfo(canonicalPath); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 2f9819997257a..2858b76ae494b 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -24,6 +24,10 @@ import static android.app.AppOpsManager.OP_LEGACY_STORAGE; import static android.app.AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE; import static android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES; import static android.app.AppOpsManager.OP_WRITE_EXTERNAL_STORAGE; +import static android.app.PendingIntent.FLAG_CANCEL_CURRENT; +import static android.app.PendingIntent.FLAG_IMMUTABLE; +import static android.app.PendingIntent.FLAG_ONE_SHOT; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 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; @@ -54,6 +58,7 @@ import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.KeyguardManager; +import android.app.PendingIntent; import android.app.admin.SecurityLog; import android.app.usage.StorageStatsManager; import android.content.BroadcastReceiver; @@ -3322,6 +3327,54 @@ class StorageManagerService extends IStorageManager.Stub } } + /* + * Returns PendingIntent which can be used by Apps with MANAGE_EXTERNAL_STORAGE permission + * to launch the manageSpaceActivity of the App specified by packageName. + */ + @Override + @Nullable + public PendingIntent getManageSpaceActivityIntent( + @NonNull String packageName, int requestCode) { + // Only Apps with MANAGE_EXTERNAL_STORAGE permission should be able to call this API. + enforcePermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE); + + // We want to call the manageSpaceActivity as a SystemService and clear identity + // of the calling App + int originalUid = Binder.getCallingUidOrThrow(); + long token = Binder.clearCallingIdentity(); + + try { + ApplicationInfo appInfo = mIPackageManager.getApplicationInfo(packageName, 0, + UserHandle.getUserId(originalUid)); + if (appInfo == null) { + throw new IllegalArgumentException( + "Invalid packageName"); + } + if (appInfo.manageSpaceActivityName == null) { + Log.i(TAG, packageName + " doesn't have a manageSpaceActivity"); + return null; + } + Context targetAppContext = mContext.createPackageContext(packageName, 0); + + Intent intent = new Intent(Intent.ACTION_DEFAULT); + intent.setClassName(packageName, + appInfo.manageSpaceActivityName); + intent.setFlags(FLAG_ACTIVITY_NEW_TASK); + + PendingIntent activity = PendingIntent.getActivity(targetAppContext, requestCode, + intent, + FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE); + return activity; + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } catch (PackageManager.NameNotFoundException e) { + throw new IllegalArgumentException( + "packageName not found"); + } finally { + Binder.restoreCallingIdentity(token); + } + } + /** Not thread safe */ class AppFuseMountScope extends AppFuseBridge.MountScope { private boolean mMounted = false;