Add ability to unmount storage volumes
Bug: 134806870
Test: manual
Change-Id: I3f283692c63324d01e25bd009a5b447ad23df323
Merged-In: I3f283692c63324d01e25bd009a5b447ad23df323
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ce060ff..5b0bb69 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -45,6 +45,7 @@
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
<uses-permission android:name="android.permission.MANAGE_USERS"/>
<uses-permission android:name="android.permission.MASTER_CLEAR" />
+ <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.NETWORK_SETTINGS" />
<uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
@@ -392,6 +393,11 @@
</intent-filter>
</receiver>
+ <!-- Exported for SystemUI to trigger -->
+ <receiver android:name=".storage.StorageUnmountReceiver"
+ android:exported="true"
+ android:permission="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
+
<!-- FileProvider to share a generated license html file.
Note that "com.android.settings.files" is set here as its authorities because a Uri
permission grant should be allowed to share a file with an external browser but it is
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 244f3bb..7b27df0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1037,6 +1037,10 @@
<string name="storage_clear_data_dlg_text">All this app\u2019s data will be deleted permanently. This includes all files, settings, accounts, databases, etc.</string>
<!-- Text for dialog if clear data fails. [CHAR LIMIT=55] -->
<string name="storage_clear_failed_dlg_text">Couldn\u2019t clear storage for app.</string>
+ <!-- Toast informing that storage unmount operation was successful. [CHAR LIMIT=80]-->
+ <string name="storage_unmount_success"><xliff:g id="name" example="SD card">%1$s</xliff:g> is safely ejected</string>
+ <!-- Toast informing that storage unmount operation failed. [CHAR LIMIT=80]-->
+ <string name="storage_unmount_failure">Couldn\u2019t safely eject <xliff:g id="name" example="SD card">%1$s</xliff:g></string>
<!-- Account management --><skip/>
<!-- Title for settings that lead into information about User's accounts [CHAR LIMIT=35] -->
diff --git a/src/com/android/car/settings/storage/StorageUnmountReceiver.java b/src/com/android/car/settings/storage/StorageUnmountReceiver.java
new file mode 100644
index 0000000..13327c7
--- /dev/null
+++ b/src/com/android/car/settings/storage/StorageUnmountReceiver.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.settings.storage;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+
+import com.android.car.settings.common.Logger;
+
+/**
+ * A {@link BroadcastReceiver} which unmounts a {@link android.os.storage.StorageVolume} based on
+ * the provided volume id.
+ */
+public class StorageUnmountReceiver extends BroadcastReceiver {
+
+ private static final Logger LOG = new Logger(StorageUnmountReceiver.class);
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ StorageManager storage = context.getSystemService(StorageManager.class);
+
+ String volId = intent.getStringExtra(VolumeInfo.EXTRA_VOLUME_ID);
+ VolumeInfo vol = storage.findVolumeById(volId);
+ if (vol != null) {
+ new UnmountTask(context, vol).execute();
+ } else {
+ LOG.w("Missing volume " + volId);
+ }
+ }
+}
diff --git a/src/com/android/car/settings/storage/UnmountTask.java b/src/com/android/car/settings/storage/UnmountTask.java
new file mode 100644
index 0000000..5e117dd
--- /dev/null
+++ b/src/com/android/car/settings/storage/UnmountTask.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.settings.storage;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+
+import com.android.car.settings.R;
+import com.android.car.settings.common.Logger;
+
+/** Task used to unmount a {@link android.os.storage.StorageVolume}. */
+public class UnmountTask extends AsyncTask<Void, Void, Exception> {
+
+ private static final Logger LOG = new Logger(UnmountTask.class);
+
+ private final Context mContext;
+ private final StorageManager mStorageManager;
+ private final String mVolumeId;
+ private final String mDescription;
+
+ public UnmountTask(Context context, VolumeInfo volume) {
+ mContext = context.getApplicationContext();
+ mStorageManager = mContext.getSystemService(StorageManager.class);
+ mVolumeId = volume.getId();
+ mDescription = mStorageManager.getBestVolumeDescription(volume);
+ }
+
+ @Override
+ protected Exception doInBackground(Void... params) {
+ try {
+ mStorageManager.unmount(mVolumeId);
+ return null;
+ } catch (Exception e) {
+ return e;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(@Nullable Exception e) {
+ if (e == null) {
+ Toast.makeText(mContext, mContext.getString(R.string.storage_unmount_success,
+ mDescription), Toast.LENGTH_SHORT).show();
+ } else {
+ LOG.e("Failed to unmount " + mVolumeId, e);
+ Toast.makeText(mContext, mContext.getString(R.string.storage_unmount_failure,
+ mDescription), Toast.LENGTH_SHORT).show();
+ }
+ }
+}