diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 16a2430a93d73..6e511f38f7e70 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -79,6 +79,7 @@ import android.view.Display; import dalvik.system.VMRuntime; import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.SomeArgs; import com.android.internal.util.Preconditions; import com.android.internal.util.UserIcons; @@ -2054,8 +2055,7 @@ final class ApplicationPackageManager extends PackageManager { /** {@hide} */ private static class MoveCallbackDelegate extends IPackageMoveObserver.Stub implements Handler.Callback { - private static final int MSG_STARTED = 1; - private static final int MSG_STATUS_CHANGED = 2; + private static final int MSG_STATUS_CHANGED = 1; final MoveCallback mCallback; final Handler mHandler; @@ -2067,26 +2067,25 @@ final class ApplicationPackageManager extends PackageManager { @Override public boolean handleMessage(Message msg) { - final int moveId = msg.arg1; switch (msg.what) { - case MSG_STARTED: - mCallback.onStarted(moveId, (String) msg.obj); - return true; case MSG_STATUS_CHANGED: - mCallback.onStatusChanged(moveId, msg.arg2, (long) msg.obj); + final SomeArgs args = (SomeArgs) msg.obj; + mCallback.onStatusChanged(args.argi1, (String) args.arg2, args.argi3, + (long) args.arg4); + args.recycle(); return true; } return false; } @Override - public void onStarted(int moveId, String title) { - mHandler.obtainMessage(MSG_STARTED, moveId, 0, title).sendToTarget(); - } - - @Override - public void onStatusChanged(int moveId, int status, long estMillis) { - mHandler.obtainMessage(MSG_STATUS_CHANGED, moveId, status, estMillis).sendToTarget(); + public void onStatusChanged(int moveId, String moveTitle, int status, long estMillis) { + final SomeArgs args = SomeArgs.obtain(); + args.argi1 = moveId; + args.arg2 = moveTitle; + args.argi3 = status; + args.arg4 = estMillis; + mHandler.obtainMessage(MSG_STATUS_CHANGED, args).sendToTarget(); } } diff --git a/core/java/android/content/pm/IPackageMoveObserver.aidl b/core/java/android/content/pm/IPackageMoveObserver.aidl index 50ab3b5c6a5f8..155ed0bf57d8d 100644 --- a/core/java/android/content/pm/IPackageMoveObserver.aidl +++ b/core/java/android/content/pm/IPackageMoveObserver.aidl @@ -22,6 +22,5 @@ package android.content.pm; * @hide */ oneway interface IPackageMoveObserver { - void onStarted(int moveId, String title); - void onStatusChanged(int moveId, int status, long estMillis); + void onStatusChanged(int moveId, String moveTitle, int status, long estMillis); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index f01ca09f3ab57..a1ee7fcf83a95 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4212,8 +4212,8 @@ public abstract class PackageManager { /** {@hide} */ public static abstract class MoveCallback { - public abstract void onStarted(int moveId, String title); - public abstract void onStatusChanged(int moveId, int status, long estMillis); + public abstract void onStatusChanged(int moveId, String moveTitle, int status, + long estMillis); } /** {@hide} */ diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index 16e0bf7476c98..fcde3f4c55c73 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -941,6 +941,24 @@ public interface IMountService extends IInterface { return _result; } + @Override + public VolumeRecord[] getVolumeRecords(int _flags) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + VolumeRecord[] _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeInt(_flags); + mRemote.transact(Stub.TRANSACTION_getVolumeRecords, _data, _reply, 0); + _reply.readException(); + _result = _reply.createTypedArray(VolumeRecord.CREATOR); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } + @Override public void mount(String volId) throws RemoteException { Parcel _data = Parcel.obtain(); @@ -1033,12 +1051,12 @@ public interface IMountService extends IInterface { } @Override - public void setVolumeNickname(String volId, String nickname) throws RemoteException { + public void setVolumeNickname(String fsUuid, String nickname) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); - _data.writeString(volId); + _data.writeString(fsUuid); _data.writeString(nickname); mRemote.transact(Stub.TRANSACTION_setVolumeNickname, _data, _reply, 0); _reply.readException(); @@ -1049,12 +1067,12 @@ public interface IMountService extends IInterface { } @Override - public void setVolumeUserFlags(String volId, int flags, int mask) throws RemoteException { + public void setVolumeUserFlags(String fsUuid, int flags, int mask) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); - _data.writeString(volId); + _data.writeString(fsUuid); _data.writeInt(flags); _data.writeInt(mask); mRemote.transact(Stub.TRANSACTION_setVolumeUserFlags, _data, _reply, 0); @@ -1065,6 +1083,21 @@ public interface IMountService extends IInterface { } } + @Override + public void forgetVolume(String fsUuid) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeString(fsUuid); + mRemote.transact(Stub.TRANSACTION_forgetVolume, _data, _reply, 0); + _reply.readException(); + } finally { + _reply.recycle(); + _data.recycle(); + } + } + @Override public String getPrimaryStorageUuid() throws RemoteException { Parcel _data = Parcel.obtain(); @@ -1192,20 +1225,22 @@ public interface IMountService extends IInterface { static final int TRANSACTION_getDisks = IBinder.FIRST_CALL_TRANSACTION + 44; static final int TRANSACTION_getVolumes = IBinder.FIRST_CALL_TRANSACTION + 45; + static final int TRANSACTION_getVolumeRecords = IBinder.FIRST_CALL_TRANSACTION + 46; - static final int TRANSACTION_mount = IBinder.FIRST_CALL_TRANSACTION + 46; - static final int TRANSACTION_unmount = IBinder.FIRST_CALL_TRANSACTION + 47; - static final int TRANSACTION_format = IBinder.FIRST_CALL_TRANSACTION + 48; + static final int TRANSACTION_mount = IBinder.FIRST_CALL_TRANSACTION + 47; + static final int TRANSACTION_unmount = IBinder.FIRST_CALL_TRANSACTION + 48; + static final int TRANSACTION_format = IBinder.FIRST_CALL_TRANSACTION + 49; - static final int TRANSACTION_partitionPublic = IBinder.FIRST_CALL_TRANSACTION + 49; - static final int TRANSACTION_partitionPrivate = IBinder.FIRST_CALL_TRANSACTION + 50; - static final int TRANSACTION_partitionMixed = IBinder.FIRST_CALL_TRANSACTION + 51; + static final int TRANSACTION_partitionPublic = IBinder.FIRST_CALL_TRANSACTION + 50; + static final int TRANSACTION_partitionPrivate = IBinder.FIRST_CALL_TRANSACTION + 51; + static final int TRANSACTION_partitionMixed = IBinder.FIRST_CALL_TRANSACTION + 52; - static final int TRANSACTION_setVolumeNickname = IBinder.FIRST_CALL_TRANSACTION + 52; - static final int TRANSACTION_setVolumeUserFlags = IBinder.FIRST_CALL_TRANSACTION + 53; + static final int TRANSACTION_setVolumeNickname = IBinder.FIRST_CALL_TRANSACTION + 53; + static final int TRANSACTION_setVolumeUserFlags = IBinder.FIRST_CALL_TRANSACTION + 54; + static final int TRANSACTION_forgetVolume = IBinder.FIRST_CALL_TRANSACTION + 55; - static final int TRANSACTION_getPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 54; - static final int TRANSACTION_setPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 55; + static final int TRANSACTION_getPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 56; + static final int TRANSACTION_setPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 57; /** * Cast an IBinder object into an IMountService interface, generating a @@ -1647,6 +1682,14 @@ public interface IMountService extends IInterface { reply.writeTypedArray(volumes, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); return true; } + case TRANSACTION_getVolumeRecords: { + data.enforceInterface(DESCRIPTOR); + int _flags = data.readInt(); + VolumeRecord[] volumes = getVolumeRecords(_flags); + reply.writeNoException(); + reply.writeTypedArray(volumes, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); + return true; + } case TRANSACTION_mount: { data.enforceInterface(DESCRIPTOR); String volId = data.readString(); @@ -1707,6 +1750,13 @@ public interface IMountService extends IInterface { reply.writeNoException(); return true; } + case TRANSACTION_forgetVolume: { + data.enforceInterface(DESCRIPTOR); + String fsUuid = data.readString(); + forgetVolume(fsUuid); + reply.writeNoException(); + return true; + } case TRANSACTION_getPrimaryStorageUuid: { data.enforceInterface(DESCRIPTOR); String volumeUuid = getPrimaryStorageUuid(); @@ -2012,6 +2062,7 @@ public interface IMountService extends IInterface { public DiskInfo[] getDisks() throws RemoteException; public VolumeInfo[] getVolumes(int flags) throws RemoteException; + public VolumeRecord[] getVolumeRecords(int flags) throws RemoteException; public void mount(String volId) throws RemoteException; public void unmount(String volId) throws RemoteException; @@ -2021,8 +2072,9 @@ public interface IMountService extends IInterface { public void partitionPrivate(String diskId) throws RemoteException; public void partitionMixed(String diskId, int ratio) throws RemoteException; - public void setVolumeNickname(String volId, String nickname) throws RemoteException; - public void setVolumeUserFlags(String volId, int flags, int mask) throws RemoteException; + public void setVolumeNickname(String fsUuid, String nickname) throws RemoteException; + public void setVolumeUserFlags(String fsUuid, int flags, int mask) throws RemoteException; + public void forgetVolume(String fsUuid) throws RemoteException; public String getPrimaryStorageUuid() throws RemoteException; public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) diff --git a/core/java/android/os/storage/IMountServiceListener.java b/core/java/android/os/storage/IMountServiceListener.java index fcb4779de3247..2d13e497d0107 100644 --- a/core/java/android/os/storage/IMountServiceListener.java +++ b/core/java/android/os/storage/IMountServiceListener.java @@ -93,8 +93,8 @@ public interface IMountServiceListener extends IInterface { } case TRANSACTION_onVolumeMetadataChanged: { data.enforceInterface(DESCRIPTOR); - final VolumeInfo vol = (VolumeInfo) data.readParcelable(null); - onVolumeMetadataChanged(vol); + final String fsUuid = data.readString(); + onVolumeMetadataChanged(fsUuid); reply.writeNoException(); return true; } @@ -192,12 +192,12 @@ public interface IMountServiceListener extends IInterface { } @Override - public void onVolumeMetadataChanged(VolumeInfo vol) throws RemoteException { + public void onVolumeMetadataChanged(String fsUuid) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); - _data.writeParcelable(vol, 0); + _data.writeString(fsUuid); mRemote.transact(Stub.TRANSACTION_onVolumeMetadataChanged, _data, _reply, android.os.IBinder.FLAG_ONEWAY); _reply.readException(); @@ -253,7 +253,7 @@ public interface IMountServiceListener extends IInterface { public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) throws RemoteException; - public void onVolumeMetadataChanged(VolumeInfo vol) throws RemoteException; + public void onVolumeMetadataChanged(String fsUuid) throws RemoteException; public void onDiskScanned(DiskInfo disk, int volumeCount) throws RemoteException; } diff --git a/core/java/android/os/storage/StorageEventListener.java b/core/java/android/os/storage/StorageEventListener.java index 6a0140e67a00c..536aca9b12c9b 100644 --- a/core/java/android/os/storage/StorageEventListener.java +++ b/core/java/android/os/storage/StorageEventListener.java @@ -41,7 +41,7 @@ public class StorageEventListener { public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { } - public void onVolumeMetadataChanged(VolumeInfo vol) { + public void onVolumeMetadataChanged(String fsUuid) { } public void onDiskScanned(DiskInfo disk, int volumeCount) { diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 6116aef86ba53..271ed9d827631 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -79,9 +79,6 @@ public class StorageManager { /** {@hide} */ public static final String UUID_PRIMARY_PHYSICAL = "primary_physical"; - /** {@hide} */ - public static final int FLAG_ALL_METADATA = 1 << 0; - private final Context mContext; private final ContentResolver mResolver; @@ -120,7 +117,7 @@ public class StorageManager { args.recycle(); return true; case MSG_VOLUME_METADATA_CHANGED: - mCallback.onVolumeMetadataChanged((VolumeInfo) args.arg1); + mCallback.onVolumeMetadataChanged((String) args.arg1); args.recycle(); return true; case MSG_DISK_SCANNED: @@ -156,9 +153,9 @@ public class StorageManager { } @Override - public void onVolumeMetadataChanged(VolumeInfo vol) { + public void onVolumeMetadataChanged(String fsUuid) { final SomeArgs args = SomeArgs.obtain(); - args.arg1 = vol; + args.arg1 = fsUuid; mHandler.obtainMessage(MSG_VOLUME_METADATA_CHANGED, args).sendToTarget(); } @@ -515,6 +512,18 @@ public class StorageManager { return null; } + /** {@hide} */ + public @Nullable VolumeRecord findRecordByUuid(String fsUuid) { + Preconditions.checkNotNull(fsUuid); + // TODO; go directly to service to make this faster + for (VolumeRecord rec : getVolumeRecords()) { + if (Objects.equals(rec.fsUuid, fsUuid)) { + return rec; + } + } + return null; + } + /** {@hide} */ public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) { return findVolumeById(emulatedVol.getId().replace("emulated", "private")); @@ -527,13 +536,17 @@ public class StorageManager { /** {@hide} */ public @NonNull List getVolumes() { - return getVolumes(0); + try { + return Arrays.asList(mMountService.getVolumes(0)); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } } /** {@hide} */ - public @NonNull List getVolumes(int flags) { + public @NonNull List getVolumeRecords() { try { - return Arrays.asList(mMountService.getVolumes(flags)); + return Arrays.asList(mMountService.getVolumeRecords(0)); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -541,13 +554,23 @@ public class StorageManager { /** {@hide} */ public @Nullable String getBestVolumeDescription(VolumeInfo vol) { - String descrip = vol.getDescription(); - if (vol.disk != null) { - if (TextUtils.isEmpty(descrip)) { - descrip = vol.disk.getDescription(); + // Nickname always takes precedence when defined + if (!TextUtils.isEmpty(vol.fsUuid)) { + final VolumeRecord rec = findRecordByUuid(vol.fsUuid); + if (!TextUtils.isEmpty(rec.nickname)) { + return rec.nickname; } } - return descrip; + + if (!TextUtils.isEmpty(vol.getDescription())) { + return vol.getDescription(); + } + + if (vol.disk != null) { + return vol.disk.getDescription(); + } + + return null; } /** {@hide} */ @@ -616,29 +639,38 @@ public class StorageManager { } /** {@hide} */ - public void setVolumeNickname(String volId, String nickname) { + public void setVolumeNickname(String fsUuid, String nickname) { try { - mMountService.setVolumeNickname(volId, nickname); + mMountService.setVolumeNickname(fsUuid, nickname); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** {@hide} */ - public void setVolumeInited(String volId, boolean inited) { + public void setVolumeInited(String fsUuid, boolean inited) { try { - mMountService.setVolumeUserFlags(volId, inited ? VolumeInfo.USER_FLAG_INITED : 0, - VolumeInfo.USER_FLAG_INITED); + mMountService.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0, + VolumeRecord.USER_FLAG_INITED); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** {@hide} */ - public void setVolumeSnoozed(String volId, boolean snoozed) { + public void setVolumeSnoozed(String fsUuid, boolean snoozed) { try { - mMountService.setVolumeUserFlags(volId, snoozed ? VolumeInfo.USER_FLAG_SNOOZED : 0, - VolumeInfo.USER_FLAG_SNOOZED); + mMountService.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0, + VolumeRecord.USER_FLAG_SNOOZED); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** {@hide} */ + public void forgetVolume(String fsUuid) { + try { + mMountService.forgetVolume(fsUuid); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java index 4e9cfc77e5bdb..fd10cae780e34 100644 --- a/core/java/android/os/storage/VolumeInfo.java +++ b/core/java/android/os/storage/VolumeInfo.java @@ -78,9 +78,6 @@ public class VolumeInfo implements Parcelable { public static final int MOUNT_FLAG_PRIMARY = 1 << 0; public static final int MOUNT_FLAG_VISIBLE = 1 << 1; - public static final int USER_FLAG_INITED = 1 << 0; - public static final int USER_FLAG_SNOOZED = 1 << 1; - private static SparseArray sStateToEnvironment = new SparseArray<>(); private static ArrayMap sEnvironmentToBroadcast = new ArrayMap<>(); @@ -135,8 +132,6 @@ public class VolumeInfo implements Parcelable { /** Framework state */ public final int mtpIndex; - public String nickname; - public int userFlags = 0; public VolumeInfo(String id, int type, DiskInfo disk, int mtpIndex) { this.id = Preconditions.checkNotNull(id); @@ -161,8 +156,6 @@ public class VolumeInfo implements Parcelable { fsLabel = parcel.readString(); path = parcel.readString(); mtpIndex = parcel.readInt(); - nickname = parcel.readString(); - userFlags = parcel.readInt(); } public static @NonNull String getEnvironmentForState(int state) { @@ -210,10 +203,6 @@ public class VolumeInfo implements Parcelable { return fsUuid; } - public @Nullable String getNickname() { - return nickname; - } - public int getMountUserId() { return mountUserId; } @@ -221,8 +210,6 @@ public class VolumeInfo implements Parcelable { public @Nullable String getDescription() { if (ID_PRIVATE_INTERNAL.equals(id)) { return Resources.getSystem().getString(com.android.internal.R.string.storage_internal); - } else if (!TextUtils.isEmpty(nickname)) { - return nickname; } else if (!TextUtils.isEmpty(fsLabel)) { return fsLabel; } else { @@ -250,14 +237,6 @@ public class VolumeInfo implements Parcelable { return (mountFlags & MOUNT_FLAG_VISIBLE) != 0; } - public boolean isInited() { - return (userFlags & USER_FLAG_INITED) != 0; - } - - public boolean isSnoozed() { - return (userFlags & USER_FLAG_SNOOZED) != 0; - } - public boolean isVisibleToUser(int userId) { if (type == TYPE_PUBLIC && userId == this.mountUserId) { return isVisible(); @@ -394,8 +373,6 @@ public class VolumeInfo implements Parcelable { pw.println(); pw.printPair("path", path); pw.printPair("mtpIndex", mtpIndex); - pw.printPair("nickname", nickname); - pw.printPair("userFlags", DebugUtils.flagsToString(getClass(), "USER_FLAG_", userFlags)); pw.decreaseIndent(); pw.println(); } @@ -461,7 +438,5 @@ public class VolumeInfo implements Parcelable { parcel.writeString(fsLabel); parcel.writeString(path); parcel.writeInt(mtpIndex); - parcel.writeString(nickname); - parcel.writeInt(userFlags); } } diff --git a/core/java/android/os/storage/VolumeRecord.java b/core/java/android/os/storage/VolumeRecord.java new file mode 100644 index 0000000000000..d12d150d58ba4 --- /dev/null +++ b/core/java/android/os/storage/VolumeRecord.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2015 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 android.os.storage; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.DebugUtils; + +import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.Preconditions; + +import java.util.Objects; + +/** + * Notes for a storage volume which may not be currently present. + * + * @hide + */ +public class VolumeRecord implements Parcelable { + public static final String EXTRA_FS_UUID = + "android.os.storage.extra.FS_UUID"; + + public static final int USER_FLAG_INITED = 1 << 0; + public static final int USER_FLAG_SNOOZED = 1 << 1; + + public final int type; + public final String fsUuid; + public String nickname; + public int userFlags; + + public VolumeRecord(int type, String fsUuid) { + this.type = type; + this.fsUuid = Preconditions.checkNotNull(fsUuid); + } + + public VolumeRecord(Parcel parcel) { + type = parcel.readInt(); + fsUuid = parcel.readString(); + nickname = parcel.readString(); + userFlags = parcel.readInt(); + } + + public int getType() { + return type; + } + + public String getFsUuid() { + return fsUuid; + } + + public String getNickname() { + return nickname; + } + + public boolean isInited() { + return (userFlags & USER_FLAG_INITED) != 0; + } + + public boolean isSnoozed() { + return (userFlags & USER_FLAG_SNOOZED) != 0; + } + + public void dump(IndentingPrintWriter pw) { + pw.println("VolumeRecord:"); + pw.increaseIndent(); + pw.printPair("type", DebugUtils.valueToString(VolumeInfo.class, "TYPE_", type)); + pw.printPair("fsUuid", fsUuid); + pw.printPair("nickname", nickname); + pw.printPair("userFlags", + DebugUtils.flagsToString(VolumeRecord.class, "USER_FLAG_", userFlags)); + pw.decreaseIndent(); + pw.println(); + } + + @Override + public VolumeRecord clone() { + final Parcel temp = Parcel.obtain(); + try { + writeToParcel(temp, 0); + temp.setDataPosition(0); + return CREATOR.createFromParcel(temp); + } finally { + temp.recycle(); + } + } + + @Override + public boolean equals(Object o) { + if (o instanceof VolumeRecord) { + return Objects.equals(fsUuid, ((VolumeRecord) o).fsUuid); + } else { + return false; + } + } + + @Override + public int hashCode() { + return fsUuid.hashCode(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public VolumeRecord createFromParcel(Parcel in) { + return new VolumeRecord(in); + } + + @Override + public VolumeRecord[] newArray(int size) { + return new VolumeRecord[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(type); + parcel.writeString(fsUuid); + parcel.writeString(nickname); + parcel.writeInt(userFlags); + } +} diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 51c2062d806e8..3146f4158eebd 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3012,6 +3012,26 @@ Explore + + %s missing + + Reinsert this device + + + Moving %s + + Moving data + + + Move complete + + Data moved to %s + + + Couldn\'t move data + + Data left at original location + No matching activities found. diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b05fe075b95c8..90437b9cd85d5 100755 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1899,6 +1899,14 @@ + + + + + + + + diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java index 240c210bd2097..deed8957173df 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java +++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java @@ -24,11 +24,17 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.MoveCallback; +import android.os.Handler; import android.os.UserHandle; import android.os.storage.DiskInfo; import android.os.storage.StorageEventListener; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; +import android.os.storage.VolumeRecord; +import android.text.TextUtils; +import android.text.format.DateUtils; import android.util.Log; import com.android.internal.R; @@ -39,12 +45,14 @@ import java.util.List; public class StorageNotification extends SystemUI { private static final String TAG = "StorageNotification"; - private static final int NOTIF_ID = 0x53544f52; // STOR + private static final int PUBLIC_ID = 0x53505542; // SPUB + private static final int PRIVATE_ID = 0x53505256; // SPRV + private static final int DISK_ID = 0x5344534b; // SDSK + private static final int MOVE_ID = 0x534d4f56; // SMOV private static final String ACTION_SNOOZE_VOLUME = "com.android.systemui.action.SNOOZE_VOLUME"; // TODO: delay some notifications to avoid bumpy fast operations - // TODO: annoy user when private media is missing private NotificationManager mNotificationManager; private StorageManager mStorageManager; @@ -52,17 +60,29 @@ public class StorageNotification extends SystemUI { private final StorageEventListener mListener = new StorageEventListener() { @Override public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { - onVolumeStateChangedInternal(vol, oldState, newState); + onVolumeStateChangedInternal(vol); } @Override - public void onVolumeMetadataChanged(VolumeInfo vol) { + public void onVolumeMetadataChanged(String fsUuid) { // Avoid kicking notifications when getting early metadata before // mounted. If already mounted, we're being kicked because of a // nickname or init'ed change. - if (vol.isMountedReadable()) { - onVolumeStateChangedInternal(vol, vol.getState(), vol.getState()); + final VolumeInfo vol = mStorageManager.findVolumeByUuid(fsUuid); + if (vol != null && vol.isMountedReadable()) { + onVolumeStateChangedInternal(vol); } + + final VolumeRecord rec = mStorageManager.findRecordByUuid(fsUuid); + if (rec == null) { + // Private volume was probably just forgotten + mNotificationManager.cancelAsUser(fsUuid, PRIVATE_ID, UserHandle.ALL); + } + } + + @Override + public void onDiskScanned(DiskInfo disk, int volumeCount) { + onDiskScannedInternal(disk, volumeCount); } }; @@ -70,8 +90,19 @@ public class StorageNotification extends SystemUI { @Override public void onReceive(Context context, Intent intent) { // TODO: kick this onto background thread - final String volId = intent.getStringExtra(VolumeInfo.EXTRA_VOLUME_ID); - mStorageManager.setVolumeSnoozed(volId, true); + final String fsUuid = intent.getStringExtra(VolumeRecord.EXTRA_FS_UUID); + mStorageManager.setVolumeSnoozed(fsUuid, true); + } + }; + + private final MoveCallback mMoveCallback = new MoveCallback() { + @Override + public void onStatusChanged(int moveId, String moveTitle, int status, long estMillis) { + if (PackageManager.isMoveStatusFinished(status)) { + onMoveFinished(moveId, moveTitle, status); + } else { + onMoveProgress(moveId, moveTitle, status, estMillis); + } } }; @@ -88,20 +119,99 @@ public class StorageNotification extends SystemUI { // Kick current state into place final List vols = mStorageManager.getVolumes(); for (VolumeInfo vol : vols) { - onVolumeStateChangedInternal(vol, vol.getState(), vol.getState()); + onVolumeStateChangedInternal(vol); + } + + mContext.getPackageManager().registerMoveCallback(mMoveCallback, new Handler()); + + updateMissingPrivateVolumes(); + } + + private void updateMissingPrivateVolumes() { + final List recs = mStorageManager.getVolumeRecords(); + for (VolumeRecord rec : recs) { + if (rec.getType() != VolumeInfo.TYPE_PRIVATE) continue; + + final String fsUuid = rec.getFsUuid(); + final VolumeInfo info = mStorageManager.findVolumeByUuid(fsUuid); + if (info != null && info.isMountedWritable()) { + // Yay, private volume is here! + mNotificationManager.cancelAsUser(fsUuid, PRIVATE_ID, UserHandle.ALL); + + } else { + // Boo, annoy the user to reinsert the private volume + final CharSequence title = mContext.getString(R.string.ext_media_missing_title, + rec.getNickname()); + final CharSequence text = mContext.getString(R.string.ext_media_missing_message); + + final Notification notif = new Notification.Builder(mContext) + .setSmallIcon(R.drawable.stat_notify_sdcard) + .setColor(mContext.getColor(R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentText(text) + .setStyle(new Notification.BigTextStyle().bigText(text)) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setLocalOnly(true) + .setContentIntent(buildForgetPendingIntent(rec)) + .setCategory(Notification.CATEGORY_SYSTEM) + .setOngoing(true) + .build(); + + mNotificationManager.notifyAsUser(fsUuid, PRIVATE_ID, notif, UserHandle.ALL); + } } } - public void onVolumeStateChangedInternal(VolumeInfo vol, int oldState, int newState) { - // We only care about public volumes - if (vol.getType() != VolumeInfo.TYPE_PUBLIC) { - return; - } + private void onDiskScannedInternal(DiskInfo disk, int volumeCount) { + if (volumeCount == 0) { + // No supported volumes found, give user option to format + final CharSequence title = mContext.getString( + R.string.ext_media_unmountable_notification_title, disk.getDescription()); + final CharSequence text = mContext.getString( + R.string.ext_media_unmountable_notification_message, disk.getDescription()); - Log.d(TAG, vol.toString()); + final Notification notif = new Notification.Builder(mContext) + .setSmallIcon(getSmallIcon(disk, VolumeInfo.STATE_UNMOUNTABLE)) + .setColor(mContext.getColor(R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentText(text) + .setStyle(new Notification.BigTextStyle().bigText(text)) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setLocalOnly(true) + .setContentIntent(buildInitPendingIntent(disk)) + .setCategory(Notification.CATEGORY_ERROR) + .build(); + + mNotificationManager.notifyAsUser(disk.getId(), DISK_ID, notif, UserHandle.ALL); + + } else { + // Yay, we have volumes! + mNotificationManager.cancelAsUser(disk.getId(), DISK_ID, UserHandle.ALL); + } + } + + private void onVolumeStateChangedInternal(VolumeInfo vol) { + switch (vol.getType()) { + case VolumeInfo.TYPE_PRIVATE: + onPrivateVolumeStateChangedInternal(vol); + break; + case VolumeInfo.TYPE_PUBLIC: + onPublicVolumeStateChangedInternal(vol); + break; + } + } + + private void onPrivateVolumeStateChangedInternal(VolumeInfo vol) { + Log.d(TAG, "Notifying about private volume: " + vol.toString()); + + updateMissingPrivateVolumes(); + } + + private void onPublicVolumeStateChangedInternal(VolumeInfo vol) { + Log.d(TAG, "Notifying about public volume: " + vol.toString()); final Notification notif; - switch (newState) { + switch (vol.getState()) { case VolumeInfo.STATE_UNMOUNTED: notif = onVolumeUnmounted(vol); break; @@ -133,9 +243,9 @@ public class StorageNotification extends SystemUI { } if (notif != null) { - mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL); + mNotificationManager.notifyAsUser(vol.getId(), PUBLIC_ID, notif, UserHandle.ALL); } else { - mNotificationManager.cancelAsUser(vol.getId(), NOTIF_ID, UserHandle.ALL); + mNotificationManager.cancelAsUser(vol.getId(), PUBLIC_ID, UserHandle.ALL); } } @@ -159,20 +269,24 @@ public class StorageNotification extends SystemUI { } private Notification onVolumeMounted(VolumeInfo vol) { + final VolumeRecord rec = mStorageManager.findRecordByUuid(vol.getFsUuid()); + // Don't annoy when user dismissed in past - if (vol.isSnoozed()) return null; + if (rec.isSnoozed()) return null; final DiskInfo disk = vol.getDisk(); - if (disk.isAdoptable() && !vol.isInited()) { + if (disk.isAdoptable() && !rec.isInited()) { final CharSequence title = disk.getDescription(); final CharSequence text = mContext.getString( R.string.ext_media_new_notification_message, disk.getDescription()); + final PendingIntent initAction = buildInitPendingIntent(vol); return buildNotificationBuilder(vol, title, text) .addAction(new Action(0, mContext.getString(R.string.ext_media_init_action), - buildInitPendingIntent(vol))) + initAction)) .addAction(new Action(0, mContext.getString(R.string.ext_media_unmount_action), buildUnmountPendingIntent(vol))) + .setContentIntent(initAction) .setDeleteIntent(buildSnoozeIntent(vol)) .setCategory(Notification.CATEGORY_SYSTEM) .build(); @@ -182,11 +296,13 @@ public class StorageNotification extends SystemUI { final CharSequence text = mContext.getString( R.string.ext_media_ready_notification_message, disk.getDescription()); + final PendingIntent browseAction = buildBrowsePendingIntent(vol); return buildNotificationBuilder(vol, title, text) .addAction(new Action(0, mContext.getString(R.string.ext_media_browse_action), - buildBrowsePendingIntent(vol))) + browseAction)) .addAction(new Action(0, mContext.getString(R.string.ext_media_unmount_action), buildUnmountPendingIntent(vol))) + .setContentIntent(browseAction) .setDeleteIntent(buildSnoozeIntent(vol)) .setCategory(Notification.CATEGORY_SYSTEM) .setPriority(Notification.PRIORITY_LOW) @@ -260,16 +376,84 @@ public class StorageNotification extends SystemUI { .build(); } - private int getSmallIcon(VolumeInfo vol) { - if (vol.disk.isSd()) { - switch (vol.getState()) { + private void onMoveProgress(int moveId, String moveTitle, int status, long estMillis) { + final CharSequence title; + if (!TextUtils.isEmpty(moveTitle)) { + title = mContext.getString(R.string.ext_media_move_specific_title, moveTitle); + } else { + title = mContext.getString(R.string.ext_media_move_title); + } + + final CharSequence text; + if (estMillis < 0) { + text = null; + } else { + text = DateUtils.formatDuration(estMillis); + } + + final Notification notif = new Notification.Builder(mContext) + .setSmallIcon(R.drawable.stat_notify_sdcard) + .setColor(mContext.getColor(R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentText(text) + .setStyle(new Notification.BigTextStyle().bigText(text)) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setLocalOnly(true) + .setCategory(Notification.CATEGORY_PROGRESS) + .setPriority(Notification.PRIORITY_LOW) + .setProgress(100, status, false) + .setOngoing(true) + .build(); + + mNotificationManager.notifyAsUser(moveTitle, MOVE_ID, notif, UserHandle.ALL); + } + + private void onMoveFinished(int moveId, String moveTitle, int status) { + if (!TextUtils.isEmpty(moveTitle)) { + // We currently ignore finished app moves; just clear the last + // published progress + mNotificationManager.cancelAsUser(moveTitle, MOVE_ID, UserHandle.ALL); + return; + } + + final VolumeInfo vol = mContext.getPackageManager().getPrimaryStorageCurrentVolume(); + final String descrip = mStorageManager.getBestVolumeDescription(vol); + + final CharSequence title; + final CharSequence text; + if (status == PackageManager.MOVE_SUCCEEDED) { + title = mContext.getString(R.string.ext_media_move_success_title); + text = mContext.getString(R.string.ext_media_move_success_message, descrip); + } else { + title = mContext.getString(R.string.ext_media_move_failure_title); + text = mContext.getString(R.string.ext_media_move_failure_message); + } + + final Notification notif = new Notification.Builder(mContext) + .setSmallIcon(R.drawable.stat_notify_sdcard) + .setColor(mContext.getColor(R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentText(text) + .setStyle(new Notification.BigTextStyle().bigText(text)) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setLocalOnly(true) + .setCategory(Notification.CATEGORY_SYSTEM) + .setPriority(Notification.PRIORITY_LOW) + .build(); + + mNotificationManager.notifyAsUser(moveTitle, MOVE_ID, notif, UserHandle.ALL); + } + + private int getSmallIcon(DiskInfo disk, int state) { + if (disk.isSd()) { + switch (state) { case VolumeInfo.STATE_CHECKING: case VolumeInfo.STATE_EJECTING: return R.drawable.stat_notify_sdcard_prepare; default: return R.drawable.stat_notify_sdcard; } - } else if (vol.disk.isUsb()) { + } else if (disk.isUsb()) { return R.drawable.stat_sys_data_usb; } else { return R.drawable.stat_notify_sdcard; @@ -279,7 +463,7 @@ public class StorageNotification extends SystemUI { private Notification.Builder buildNotificationBuilder(VolumeInfo vol, CharSequence title, CharSequence text) { return new Notification.Builder(mContext) - .setSmallIcon(getSmallIcon(vol)) + .setSmallIcon(getSmallIcon(vol.getDisk(), vol.getState())) .setColor(mContext.getColor(R.color.system_notification_accent_color)) .setContentTitle(title) .setContentText(text) @@ -288,6 +472,17 @@ public class StorageNotification extends SystemUI { .setLocalOnly(true); } + private PendingIntent buildInitPendingIntent(DiskInfo disk) { + final Intent intent = new Intent(); + intent.setClassName("com.android.settings", + "com.android.settings.deviceinfo.StorageWizardInit"); + intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.getId()); + + final int requestKey = disk.getId().hashCode(); + return PendingIntent.getActivityAsUser(mContext, requestKey, intent, + PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); + } + private PendingIntent buildInitPendingIntent(VolumeInfo vol) { final Intent intent = new Intent(); intent.setClassName("com.android.settings", @@ -321,7 +516,7 @@ public class StorageNotification extends SystemUI { private PendingIntent buildDetailsPendingIntent(VolumeInfo vol) { final Intent intent = new Intent(); intent.setClassName("com.android.settings", - "com.android.settings.Settings$StorageVolumeSettingsActivity"); + "com.android.settings.Settings$PublicVolumeSettingsActivity"); intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId()); final int requestKey = vol.getId().hashCode(); @@ -331,10 +526,21 @@ public class StorageNotification extends SystemUI { private PendingIntent buildSnoozeIntent(VolumeInfo vol) { final Intent intent = new Intent(ACTION_SNOOZE_VOLUME); - intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId()); + intent.putExtra(VolumeRecord.EXTRA_FS_UUID, vol.getFsUuid()); final int requestKey = vol.getId().hashCode(); return PendingIntent.getBroadcastAsUser(mContext, requestKey, intent, PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.CURRENT); } + + private PendingIntent buildForgetPendingIntent(VolumeRecord rec) { + final Intent intent = new Intent(); + intent.setClassName("com.android.settings", + "com.android.settings.Settings$PrivateVolumeForgetActivity"); + intent.putExtra(VolumeRecord.EXTRA_FS_UUID, rec.getFsUuid()); + + final int requestKey = rec.getFsUuid().hashCode(); + return PendingIntent.getActivityAsUser(mContext, requestKey, intent, + PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); + } } diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index 643ff5f783365..74adeb7f5b965 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -62,11 +62,11 @@ import android.os.storage.StorageManager; import android.os.storage.StorageResultCode; import android.os.storage.StorageVolume; import android.os.storage.VolumeInfo; +import android.os.storage.VolumeRecord; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.AtomicFile; -import android.util.DebugUtils; import android.util.Log; import android.util.Slog; import android.util.Xml; @@ -251,49 +251,7 @@ class MountService extends IMountService.Stub private static final String ATTR_NICKNAME = "nickname"; private static final String ATTR_USER_FLAGS = "userFlags"; - private final AtomicFile mMetadataFile; - - private static class VolumeMetadata { - public final int type; - public final String fsUuid; - public String nickname; - public int userFlags; - - public VolumeMetadata(int type, String fsUuid) { - this.type = type; - this.fsUuid = Preconditions.checkNotNull(fsUuid); - } - - public static VolumeMetadata read(XmlPullParser in) throws IOException { - final int type = readIntAttribute(in, ATTR_TYPE); - final String fsUuid = readStringAttribute(in, ATTR_FS_UUID); - final VolumeMetadata meta = new VolumeMetadata(type, fsUuid); - meta.nickname = readStringAttribute(in, ATTR_NICKNAME); - meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS); - return meta; - } - - public static void write(XmlSerializer out, VolumeMetadata meta) throws IOException { - out.startTag(null, TAG_VOLUME); - writeIntAttribute(out, ATTR_TYPE, meta.type); - writeStringAttribute(out, ATTR_FS_UUID, meta.fsUuid); - writeStringAttribute(out, ATTR_NICKNAME, meta.nickname); - writeIntAttribute(out, ATTR_USER_FLAGS, meta.userFlags); - out.endTag(null, TAG_VOLUME); - } - - public void dump(IndentingPrintWriter pw) { - pw.println("VolumeMetadata:"); - pw.increaseIndent(); - pw.printPair("type", DebugUtils.valueToString(VolumeInfo.class, "TYPE_", type)); - pw.printPair("fsUuid", fsUuid); - pw.printPair("nickname", nickname); - pw.printPair("userFlags", - DebugUtils.flagsToString(VolumeInfo.class, "USER_FLAG_", userFlags)); - pw.decreaseIndent(); - pw.println(); - } - } + private final AtomicFile mSettingsFile; /** * Never hold the lock while performing downcalls into vold, since @@ -311,9 +269,9 @@ class MountService extends IMountService.Stub @GuardedBy("mLock") private ArrayMap mVolumes = new ArrayMap<>(); - /** Map from UUID to metadata */ + /** Map from UUID to record */ @GuardedBy("mLock") - private ArrayMap mMetadata = new ArrayMap<>(); + private ArrayMap mRecords = new ArrayMap<>(); @GuardedBy("mLock") private String mPrimaryStorageUuid; @@ -370,15 +328,6 @@ class MountService extends IMountService.Stub } } - private VolumeMetadata findOrCreateMetadataLocked(VolumeInfo vol) { - VolumeMetadata meta = mMetadata.get(vol.fsUuid); - if (meta == null) { - meta = new VolumeMetadata(vol.type, vol.fsUuid); - mMetadata.put(meta.fsUuid, meta); - } - return meta; - } - private CountDownLatch findOrCreateDiskScanLatch(String diskId) { synchronized (mLock) { CountDownLatch latch = mDiskScanLatches.get(diskId); @@ -913,7 +862,7 @@ class MountService extends IMountService.Stub final int oldState = vol.state; final int newState = Integer.parseInt(cooked[2]); vol.state = newState; - onVolumeStateChangedLocked(vol.clone(), oldState, newState); + onVolumeStateChangedLocked(vol, oldState, newState); } break; } @@ -923,7 +872,6 @@ class MountService extends IMountService.Stub if (vol != null) { vol.fsType = cooked[2]; } - mCallbacks.notifyVolumeMetadataChanged(vol.clone()); break; } case VoldResponseCode.VOLUME_FS_UUID_CHANGED: { @@ -932,8 +880,6 @@ class MountService extends IMountService.Stub if (vol != null) { vol.fsUuid = cooked[2]; } - refreshMetadataLocked(); - mCallbacks.notifyVolumeMetadataChanged(vol.clone()); break; } case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: { @@ -945,7 +891,7 @@ class MountService extends IMountService.Stub } vol.fsLabel = builder.toString().trim(); } - mCallbacks.notifyVolumeMetadataChanged(vol.clone()); + // TODO: notify listeners that label changed break; } case VoldResponseCode.VOLUME_PATH_CHANGED: { @@ -1070,6 +1016,19 @@ class MountService extends IMountService.Stub } private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) { + // Remember that we saw this volume so we're ready to accept user + // metadata, or so we can annoy them when a private volume is ejected + if (vol.isMountedReadable() && !TextUtils.isEmpty(vol.fsUuid)) { + if (!mRecords.containsKey(vol.fsUuid)) { + final VolumeRecord rec = new VolumeRecord(vol.type, vol.fsUuid); + if (vol.type == VolumeInfo.TYPE_PRIVATE) { + rec.nickname = vol.disk.getDescription(); + } + mRecords.put(rec.fsUuid, rec); + writeSettingsLocked(); + } + } + mCallbacks.notifyVolumeStateChanged(vol, oldState, newState); if (isBroadcastWorthy(vol)) { @@ -1117,7 +1076,7 @@ class MountService extends IMountService.Stub // TODO: estimate remaining time try { - mMoveCallback.onStatusChanged(-1, status, -1); + mMoveCallback.onStatusChanged(-1, null, status, -1); } catch (RemoteException ignored) { } @@ -1127,7 +1086,7 @@ class MountService extends IMountService.Stub Slog.d(TAG, "Move to " + mMoveTargetUuid + " copy phase finshed; persisting"); mPrimaryStorageUuid = mMoveTargetUuid; - writeMetadataLocked(); + writeSettingsLocked(); } if (PackageManager.isMoveStatusFinished(status)) { @@ -1138,25 +1097,6 @@ class MountService extends IMountService.Stub } } - /** - * Refresh latest metadata into any currently active {@link VolumeInfo}. - */ - private void refreshMetadataLocked() { - final int size = mVolumes.size(); - for (int i = 0; i < size; i++) { - final VolumeInfo vol = mVolumes.valueAt(i); - final VolumeMetadata meta = mMetadata.get(vol.fsUuid); - - if (meta != null) { - vol.nickname = meta.nickname; - vol.userFlags = meta.userFlags; - } else { - vol.nickname = null; - vol.userFlags = 0; - } - } - } - private void enforcePermission(String perm) { mContext.enforceCallingOrSelfPermission(perm, perm); } @@ -1205,11 +1145,11 @@ class MountService extends IMountService.Stub mLastMaintenance = mLastMaintenanceFile.lastModified(); } - mMetadataFile = new AtomicFile( + mSettingsFile = new AtomicFile( new File(Environment.getSystemSecureDirectory(), "storage.xml")); synchronized (mLock) { - readMetadataLocked(); + readSettingsLocked(); } /* @@ -1235,12 +1175,12 @@ class MountService extends IMountService.Stub mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget(); } - private void readMetadataLocked() { - mMetadata.clear(); + private void readSettingsLocked() { + mRecords.clear(); FileInputStream fis = null; try { - fis = mMetadataFile.openRead(); + fis = mSettingsFile.openRead(); final XmlPullParser in = Xml.newPullParser(); in.setInput(fis, null); @@ -1263,8 +1203,8 @@ class MountService extends IMountService.Stub } } else if (TAG_VOLUME.equals(tag)) { - final VolumeMetadata meta = VolumeMetadata.read(in); - mMetadata.put(meta.fsUuid, meta); + final VolumeRecord rec = readVolumeRecord(in); + mRecords.put(rec.fsUuid, rec); } } } @@ -1279,10 +1219,10 @@ class MountService extends IMountService.Stub } } - private void writeMetadataLocked() { + private void writeSettingsLocked() { FileOutputStream fos = null; try { - fos = mMetadataFile.startWrite(); + fos = mSettingsFile.startWrite(); XmlSerializer out = new FastXmlSerializer(); out.setOutput(fos, "utf-8"); @@ -1290,22 +1230,40 @@ class MountService extends IMountService.Stub out.startTag(null, TAG_VOLUMES); writeIntAttribute(out, ATTR_VERSION, VERSION_ADD_PRIMARY); writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid); - final int size = mMetadata.size(); + final int size = mRecords.size(); for (int i = 0; i < size; i++) { - final VolumeMetadata meta = mMetadata.valueAt(i); - VolumeMetadata.write(out, meta); + final VolumeRecord rec = mRecords.valueAt(i); + writeVolumeRecord(out, rec); } out.endTag(null, TAG_VOLUMES); out.endDocument(); - mMetadataFile.finishWrite(fos); + mSettingsFile.finishWrite(fos); } catch (IOException e) { if (fos != null) { - mMetadataFile.failWrite(fos); + mSettingsFile.failWrite(fos); } } } + public static VolumeRecord readVolumeRecord(XmlPullParser in) throws IOException { + final int type = readIntAttribute(in, ATTR_TYPE); + final String fsUuid = readStringAttribute(in, ATTR_FS_UUID); + final VolumeRecord meta = new VolumeRecord(type, fsUuid); + meta.nickname = readStringAttribute(in, ATTR_NICKNAME); + meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS); + return meta; + } + + public static void writeVolumeRecord(XmlSerializer out, VolumeRecord rec) throws IOException { + out.startTag(null, TAG_VOLUME); + writeIntAttribute(out, ATTR_TYPE, rec.type); + writeStringAttribute(out, ATTR_FS_UUID, rec.fsUuid); + writeStringAttribute(out, ATTR_NICKNAME, rec.nickname); + writeIntAttribute(out, ATTR_USER_FLAGS, rec.userFlags); + out.endTag(null, TAG_VOLUME); + } + /** * Exposed API calls below here */ @@ -1471,32 +1429,40 @@ class MountService extends IMountService.Stub } @Override - public void setVolumeNickname(String volId, String nickname) { + public void setVolumeNickname(String fsUuid, String nickname) { enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); waitForReady(); synchronized (mLock) { - final VolumeInfo vol = findVolumeById(volId); - final VolumeMetadata meta = findOrCreateMetadataLocked(vol); - meta.nickname = nickname; - refreshMetadataLocked(); - writeMetadataLocked(); - mCallbacks.notifyVolumeMetadataChanged(vol.clone()); + final VolumeRecord rec = mRecords.get(fsUuid); + rec.nickname = nickname; + mCallbacks.notifyVolumeMetadataChanged(fsUuid); + writeSettingsLocked(); } } @Override - public void setVolumeUserFlags(String volId, int flags, int mask) { + public void setVolumeUserFlags(String fsUuid, int flags, int mask) { enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); waitForReady(); synchronized (mLock) { - final VolumeInfo vol = findVolumeById(volId); - final VolumeMetadata meta = findOrCreateMetadataLocked(vol); - meta.userFlags = (meta.userFlags & ~mask) | (flags & mask); - refreshMetadataLocked(); - writeMetadataLocked(); - mCallbacks.notifyVolumeMetadataChanged(vol.clone()); + final VolumeRecord rec = mRecords.get(fsUuid); + rec.userFlags = (rec.userFlags & ~mask) | (flags & mask); + mCallbacks.notifyVolumeMetadataChanged(fsUuid); + writeSettingsLocked(); + } + } + + @Override + public void forgetVolume(String fsUuid) { + enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); + waitForReady(); + + synchronized (mLock) { + mRecords.remove(fsUuid); + mCallbacks.notifyVolumeMetadataChanged(fsUuid); + writeSettingsLocked(); } } @@ -2329,11 +2295,6 @@ class MountService extends IMountService.Stub @Override public VolumeInfo[] getVolumes(int flags) { - if ((flags & StorageManager.FLAG_ALL_METADATA) != 0) { - // TODO: implement support for returning all metadata - throw new UnsupportedOperationException(); - } - synchronized (mLock) { final VolumeInfo[] res = new VolumeInfo[mVolumes.size()]; for (int i = 0; i < mVolumes.size(); i++) { @@ -2343,6 +2304,17 @@ class MountService extends IMountService.Stub } } + @Override + public VolumeRecord[] getVolumeRecords(int flags) { + synchronized (mLock) { + final VolumeRecord[] res = new VolumeRecord[mRecords.size()]; + for (int i = 0; i < mRecords.size(); i++) { + res[i] = mRecords.valueAt(i); + } + return res; + } + } + private void addObbStateLocked(ObbState obbState) throws RemoteException { final IBinder binder = obbState.getBinder(); List obbStates = mObbMounts.get(binder); @@ -2892,7 +2864,7 @@ class MountService extends IMountService.Stub break; } case MSG_VOLUME_METADATA_CHANGED: { - callback.onVolumeMetadataChanged((VolumeInfo) args.arg1); + callback.onVolumeMetadataChanged((String) args.arg1); break; } case MSG_DISK_SCANNED: { @@ -2912,21 +2884,21 @@ class MountService extends IMountService.Stub private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { final SomeArgs args = SomeArgs.obtain(); - args.arg1 = vol; + args.arg1 = vol.clone(); args.argi2 = oldState; args.argi3 = newState; obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget(); } - private void notifyVolumeMetadataChanged(VolumeInfo vol) { + private void notifyVolumeMetadataChanged(String fsUuid) { final SomeArgs args = SomeArgs.obtain(); - args.arg1 = vol; + args.arg1 = fsUuid; obtainMessage(MSG_VOLUME_METADATA_CHANGED, args).sendToTarget(); } private void notifyDiskScanned(DiskInfo disk, int volumeCount) { final SomeArgs args = SomeArgs.obtain(); - args.arg1 = disk; + args.arg1 = disk.clone(); args.argi2 = volumeCount; obtainMessage(MSG_DISK_SCANNED, args).sendToTarget(); } @@ -2937,10 +2909,10 @@ class MountService extends IMountService.Stub mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); for (String arg : args) { - if ("--clear-metadata".equals(arg)) { + if ("--clear".equals(arg)) { synchronized (mLock) { - mMetadata.clear(); - writeMetadataLocked(); + mRecords.clear(); + writeSettingsLocked(); } } } @@ -2966,11 +2938,11 @@ class MountService extends IMountService.Stub pw.decreaseIndent(); pw.println(); - pw.println("Metadata:"); + pw.println("Records:"); pw.increaseIndent(); - for (int i = 0; i < mMetadata.size(); i++) { - final VolumeMetadata meta = mMetadata.valueAt(i); - meta.dump(pw); + for (int i = 0; i < mRecords.size(); i++) { + final VolumeRecord note = mRecords.valueAt(i); + note.dump(pw); } pw.decreaseIndent(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6c18e25a29021..26f8f70cbbcff 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -14192,7 +14192,8 @@ public class PackageManagerService extends IPackageManager.Stub { movePackageInternal(packageName, volumeUuid, moveId); } catch (PackageManagerException e) { Slog.d(TAG, "Failed to move " + packageName, e); - mMoveCallbacks.notifyStatusChanged(moveId, PackageManager.MOVE_FAILED_INTERNAL_ERROR); + mMoveCallbacks.notifyStatusChanged(moveId, null, + PackageManager.MOVE_FAILED_INTERNAL_ERROR); } return moveId; } @@ -14209,6 +14210,7 @@ public class PackageManagerService extends IPackageManager.Stub { final String packageAbiOverride; final int appId; final String seinfo; + final String moveTitle; // reader synchronized (mPackages) { @@ -14228,9 +14230,6 @@ public class PackageManagerService extends IPackageManager.Stub { // TODO: yell if already in desired location - mMoveCallbacks.notifyStarted(moveId, - String.valueOf(pm.getApplicationLabel(pkg.applicationInfo))); - pkg.mOperationPending = true; currentAsec = pkg.applicationInfo.isForwardLocked() @@ -14241,6 +14240,7 @@ public class PackageManagerService extends IPackageManager.Stub { packageAbiOverride = ps.cpuAbiOverrideString; appId = UserHandle.getAppId(pkg.applicationInfo.uid); seinfo = pkg.applicationInfo.seinfo; + moveTitle = String.valueOf(pm.getApplicationLabel(pkg.applicationInfo)); } int installFlags; @@ -14268,7 +14268,7 @@ public class PackageManagerService extends IPackageManager.Stub { } Slog.d(TAG, "Moving " + packageName + " from " + currentVolumeUuid + " to " + volumeUuid); - mMoveCallbacks.notifyStatusChanged(moveId, 10, -1); + mMoveCallbacks.notifyStatusChanged(moveId, moveTitle, 10); if (moveData) { synchronized (mInstallLock) { @@ -14288,7 +14288,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } - mMoveCallbacks.notifyStatusChanged(moveId, 50); + mMoveCallbacks.notifyStatusChanged(moveId, moveTitle, 50); final IPackageInstallObserver2 installObserver = new IPackageInstallObserver2.Stub() { @Override @@ -14315,15 +14315,15 @@ public class PackageManagerService extends IPackageManager.Stub { final int status = PackageManager.installStatusToPublicStatus(returnCode); switch (status) { case PackageInstaller.STATUS_SUCCESS: - mMoveCallbacks.notifyStatusChanged(moveId, + mMoveCallbacks.notifyStatusChanged(moveId, moveTitle, PackageManager.MOVE_SUCCEEDED); break; case PackageInstaller.STATUS_FAILURE_STORAGE: - mMoveCallbacks.notifyStatusChanged(moveId, + mMoveCallbacks.notifyStatusChanged(moveId, moveTitle, PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE); break; default: - mMoveCallbacks.notifyStatusChanged(moveId, + mMoveCallbacks.notifyStatusChanged(moveId, moveTitle, PackageManager.MOVE_FAILED_INTERNAL_ERROR); break; } @@ -14346,15 +14346,12 @@ public class PackageManagerService extends IPackageManager.Stub { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null); final int realMoveId = mNextMoveId.getAndIncrement(); + final String realTitle = null; + final IPackageMoveObserver callback = new IPackageMoveObserver.Stub() { @Override - public void onStarted(int moveId, String title) { - // Ignored - } - - @Override - public void onStatusChanged(int moveId, int status, long estMillis) { - mMoveCallbacks.notifyStatusChanged(realMoveId, status, estMillis); + public void onStatusChanged(int moveId, String title, int status, long estMillis) { + mMoveCallbacks.notifyStatusChanged(realMoveId, realTitle, status, estMillis); } }; @@ -14709,7 +14706,6 @@ public class PackageManagerService extends IPackageManager.Stub { } private static class MoveCallbacks extends Handler { - private static final int MSG_STARTED = 1; private static final int MSG_STATUS_CHANGED = 2; private final RemoteCallbackList @@ -14747,37 +14743,26 @@ public class PackageManagerService extends IPackageManager.Stub { private void invokeCallback(IPackageMoveObserver callback, int what, SomeArgs args) throws RemoteException { switch (what) { - case MSG_STARTED: { - callback.onStarted(args.argi1, (String) args.arg2); - break; - } case MSG_STATUS_CHANGED: { - callback.onStatusChanged(args.argi1, args.argi2, (long) args.arg3); + callback.onStatusChanged(args.argi1, (String) args.arg2, args.argi3, + (long) args.arg4); break; } } } - private void notifyStarted(int moveId, String title) { - Slog.v(TAG, "Move " + moveId + " started with title " + title); - - final SomeArgs args = SomeArgs.obtain(); - args.argi1 = moveId; - args.arg2 = title; - obtainMessage(MSG_STARTED, args).sendToTarget(); + private void notifyStatusChanged(int moveId, String moveTitle, int status) { + notifyStatusChanged(moveId, moveTitle, status, -1); } - private void notifyStatusChanged(int moveId, int status) { - notifyStatusChanged(moveId, status, -1); - } - - private void notifyStatusChanged(int moveId, int status, long estMillis) { + private void notifyStatusChanged(int moveId, String moveTitle, int status, long estMillis) { Slog.v(TAG, "Move " + moveId + " status " + status); final SomeArgs args = SomeArgs.obtain(); args.argi1 = moveId; - args.argi2 = status; - args.arg3 = estMillis; + args.arg2 = moveTitle; + args.argi3 = status; + args.arg4 = estMillis; obtainMessage(MSG_STATUS_CHANGED, args).sendToTarget(); synchronized (mLastStatus) {