From a83bf1966e3e51fb052db86daf359d5b1cc110f5 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 8 Jul 2015 09:18:20 -0700 Subject: [PATCH 1/2] Use best volume description for MTP. Otherwise we end up showing adopted storage devices as "Unknown." Bug: 20275423 Change-Id: Ib42474fd5b3284b1e8eca7de8a4cfbb71a34a107 --- core/java/android/os/storage/VolumeInfo.java | 25 ++++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java index a4ee8b774a9e6..32f7bc93e3930 100644 --- a/core/java/android/os/storage/VolumeInfo.java +++ b/core/java/android/os/storage/VolumeInfo.java @@ -21,7 +21,6 @@ import android.annotation.Nullable; import android.content.Context; import android.content.Intent; import android.content.res.Resources; -import android.mtp.MtpStorage; import android.net.Uri; import android.os.Environment; import android.os.Parcel; @@ -300,6 +299,8 @@ public class VolumeInfo implements Parcelable { } public StorageVolume buildStorageVolume(Context context, int userId) { + final StorageManager storage = context.getSystemService(StorageManager.class); + final boolean removable; final boolean emulated; final boolean allowMassStorage = false; @@ -310,14 +311,7 @@ public class VolumeInfo implements Parcelable { userPath = new File("/dev/null"); } - String description = getDescription(); - if (description == null) { - description = getFsUuid(); - } - if (description == null) { - description = context.getString(android.R.string.unknownName); - } - + String description = null; long mtpReserveSize = 0; long maxFileSize = 0; int mtpStorageId = StorageVolume.STORAGE_ID_INVALID; @@ -325,11 +319,16 @@ public class VolumeInfo implements Parcelable { if (type == TYPE_EMULATED) { emulated = true; + final VolumeInfo privateVol = storage.findPrivateForEmulated(this); + if (privateVol != null) { + description = storage.getBestVolumeDescription(privateVol); + } + if (isPrimary()) { mtpStorageId = StorageVolume.STORAGE_ID_PRIMARY; } - mtpReserveSize = StorageManager.from(context).getStorageLowBytes(userPath); + mtpReserveSize = storage.getStorageLowBytes(userPath); if (ID_EMULATED_INTERNAL.equals(id)) { removable = false; @@ -341,6 +340,8 @@ public class VolumeInfo implements Parcelable { emulated = false; removable = true; + description = storage.getBestVolumeDescription(this); + if (isPrimary()) { mtpStorageId = StorageVolume.STORAGE_ID_PRIMARY; } else { @@ -357,6 +358,10 @@ public class VolumeInfo implements Parcelable { throw new IllegalStateException("Unexpected volume type " + type); } + if (description == null) { + description = context.getString(android.R.string.unknownName); + } + return new StorageVolume(id, mtpStorageId, userPath, description, isPrimary(), removable, emulated, mtpReserveSize, allowMassStorage, maxFileSize, new UserHandle(userId), fsUuid, envState); From 14cbe52150bad38ab7c14c868e25a68d354f8282 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 8 Jul 2015 14:06:37 -0700 Subject: [PATCH 2/2] Kill MediaProvider during drastic changes. Sadly MediaProvider makes a ton of assumptions about storage paths not changing. To ensure that it picks up radical storage changes, kill it and let it restart to pick up new paths. Also give ourselves a longer timeout when benchmarking. Bug: 20275423 Change-Id: I9971c4667dabdc685cb23528443f085f152c461d --- .../java/com/android/server/MountService.java | 30 ++++++++++++++++--- .../android/server/NativeDaemonConnector.java | 17 +++++++---- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index c7ca1ab19d315..25bd78732615c 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -31,6 +31,7 @@ import android.Manifest; import android.annotation.Nullable; import android.app.ActivityManagerNative; import android.app.AppOpsManager; +import android.app.IActivityManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -39,10 +40,10 @@ import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.IPackageMoveObserver; import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; import android.content.res.ObbInfo; -import android.mtp.MtpStorage; import android.net.Uri; import android.os.Binder; import android.os.DropBoxManager; @@ -53,7 +54,6 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; -import android.os.Looper; import android.os.Message; import android.os.Process; import android.os.RemoteCallbackList; @@ -73,6 +73,7 @@ import android.os.storage.StorageResultCode; import android.os.storage.StorageVolume; import android.os.storage.VolumeInfo; import android.os.storage.VolumeRecord; +import android.provider.MediaStore; import android.provider.Settings; import android.text.TextUtils; import android.text.format.DateUtils; @@ -176,7 +177,6 @@ class MountService extends IMountService.Stub } } - private static final boolean LOCAL_LOGD = false; private static final boolean DEBUG_EVENTS = false; private static final boolean DEBUG_OBB = false; @@ -723,10 +723,30 @@ class MountService extends IMountService.Stub MountServiceIdler.scheduleIdlePass(mContext); } + /** + * MediaProvider has a ton of code that makes assumptions about storage + * paths never changing, so we outright kill them to pick up new state. + */ + @Deprecated + private void killMediaProvider() { + final ProviderInfo provider = mPms.resolveContentProvider(MediaStore.AUTHORITY, 0, + UserHandle.USER_OWNER); + if (provider != null) { + final IActivityManager am = ActivityManagerNative.getDefault(); + try { + am.killApplicationWithAppId(provider.applicationInfo.packageName, + UserHandle.getAppId(provider.applicationInfo.uid), "vold reset"); + } catch (RemoteException e) { + } + } + } + private void resetIfReadyAndConnectedLocked() { Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady + ", mDaemonConnected=" + mDaemonConnected); if (mSystemReady && mDaemonConnected) { + killMediaProvider(); + mDisks.clear(); mVolumes.clear(); @@ -1606,7 +1626,9 @@ class MountService extends IMountService.Stub waitForReady(); try { - final NativeDaemonEvent res = mConnector.execute("volume", "benchmark", volId); + // TODO: make benchmark async so we don't block other commands + final NativeDaemonEvent res = mConnector.execute(3 * DateUtils.MINUTE_IN_MILLIS, + "volume", "benchmark", volId); return Long.parseLong(res.getMessage()); } catch (NativeDaemonTimeoutException e) { return Long.MAX_VALUE; diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java index e7979e4310b0e..519a2a3f4f887 100644 --- a/services/core/java/com/android/server/NativeDaemonConnector.java +++ b/services/core/java/com/android/server/NativeDaemonConnector.java @@ -69,7 +69,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo private AtomicInteger mSequenceNumber; - private static final int DEFAULT_TIMEOUT = 1 * 60 * 1000; /* 1 minute */ + private static final long DEFAULT_TIMEOUT = 1 * 60 * 1000; /* 1 minute */ private static final long WARN_EXECUTE_DELAY_MS = 500; /* .5 sec */ /** Lock held whenever communicating with native daemon. */ @@ -337,7 +337,12 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo */ public NativeDaemonEvent execute(String cmd, Object... args) throws NativeDaemonConnectorException { - final NativeDaemonEvent[] events = executeForList(cmd, args); + return execute(DEFAULT_TIMEOUT, cmd, args); + } + + public NativeDaemonEvent execute(long timeoutMs, String cmd, Object... args) + throws NativeDaemonConnectorException { + final NativeDaemonEvent[] events = executeForList(timeoutMs, cmd, args); if (events.length != 1) { throw new NativeDaemonConnectorException( "Expected exactly one response, but received " + events.length); @@ -372,7 +377,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo */ public NativeDaemonEvent[] executeForList(String cmd, Object... args) throws NativeDaemonConnectorException { - return execute(DEFAULT_TIMEOUT, cmd, args); + return executeForList(DEFAULT_TIMEOUT, cmd, args); } /** @@ -387,7 +392,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo * {@link NativeDaemonEvent#isClassClientError()} or * {@link NativeDaemonEvent#isClassServerError()}. */ - public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args) + public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args) throws NativeDaemonConnectorException { final long startTime = SystemClock.elapsedRealtime(); @@ -418,7 +423,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo NativeDaemonEvent event = null; do { - event = mResponseQueue.remove(sequenceNumber, timeout, logCmd); + event = mResponseQueue.remove(sequenceNumber, timeoutMs, logCmd); if (event == null) { loge("timed-out waiting for response to " + logCmd); throw new NativeDaemonTimeoutException(logCmd, event); @@ -606,7 +611,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo // note that the timeout does not count time in deep sleep. If you don't want // the device to sleep, hold a wakelock - public NativeDaemonEvent remove(int cmdNum, int timeoutMs, String logCmd) { + public NativeDaemonEvent remove(int cmdNum, long timeoutMs, String logCmd) { PendingCmd found = null; synchronized (mPendingCmds) { for (PendingCmd pendingCmd : mPendingCmds) {