Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 38cf8867 authored by Kenny Root's avatar Kenny Root
Browse files

Remove OBBs from state list when volume unmounted

Don't keep tracking OBBs when the volume they're located on goes away.
Remove them from our state tracking maps and then send a notification to
any listener that is still around.

Add a dump handler to MountService so the state of the mount lists
can be inspected.

Change the API to just make a callback directly to the change listener
when mount is called when it's already mounted or unmount called when
it's already unmounted.

Change-Id: Idb4afbb943ca5ca775825f908bff334e3ce1cfcc
parent 063d02bb
Loading
Loading
Loading
Loading
+10 −2
Original line number Diff line number Diff line
@@ -52720,6 +52720,16 @@
 visibility="public"
>
</field>
<field name="filename"
 type="java.lang.String"
 transient="false"
 volatile="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="flags"
 type="int"
 transient="false"
@@ -129289,8 +129299,6 @@
</parameter>
<parameter name="listener" type="android.os.storage.OnObbStateChangeListener">
</parameter>
<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
</exception>
</method>
</class>
</package>
+7 −0
Original line number Diff line number Diff line
@@ -28,6 +28,11 @@ public class ObbInfo implements Parcelable {
    /** Flag noting that this OBB is an overlay patch for a base OBB. */
    public static final int OBB_OVERLAY = 1 << 0;

    /**
     * The canonical filename of the OBB.
     */
    public String filename;

    /**
     * The name of the package to which the OBB file belongs.
     */
@@ -66,6 +71,7 @@ public class ObbInfo implements Parcelable {
    }

    public void writeToParcel(Parcel dest, int parcelableFlags) {
        dest.writeString(filename);
        dest.writeString(packageName);
        dest.writeInt(version);
        dest.writeInt(flags);
@@ -83,6 +89,7 @@ public class ObbInfo implements Parcelable {
    };

    private ObbInfo(Parcel source) {
        filename = source.readString();
        packageName = source.readString();
        version = source.readInt();
        flags = source.readInt();
+5 −0
Original line number Diff line number Diff line
@@ -45,9 +45,14 @@ public class ObbScanner {
            throw new IllegalArgumentException("OBB file does not exist: " + filePath);
        }

        /*
         * XXX This will fail to find the real canonical path if bind mounts are
         * used, but we don't use any bind mounts right now.
         */
        final String canonicalFilePath = obbFile.getCanonicalPath();

        ObbInfo obbInfo = new ObbInfo();
        obbInfo.filename = canonicalFilePath;
        getObbInfo_native(canonicalFilePath, obbInfo);

        return obbInfo;
+1 −4
Original line number Diff line number Diff line
@@ -434,7 +434,6 @@ public class StorageManager
     * @param key secret used to encrypt the OBB; may be <code>null</code> if no
     *            encryption was used on the OBB.
     * @return whether the mount call was successfully queued or not
     * @throws IllegalArgumentException when the OBB is already mounted
     */
    public boolean mountObb(String filename, String key, OnObbStateChangeListener listener) {
        try {
@@ -468,10 +467,8 @@ public class StorageManager
     * @param force whether to kill any programs using this in order to unmount
     *            it
     * @return whether the unmount call was successfully queued or not
     * @throws IllegalArgumentException when OBB is not already mounted
     */
    public boolean unmountObb(String filename, boolean force, OnObbStateChangeListener listener)
            throws IllegalArgumentException {
    public boolean unmountObb(String filename, boolean force, OnObbStateChangeListener listener) {
        try {
            mObbActionListener.addListener(listener);
            mMountService.unmountObb(filename, force, mObbActionListener);
+230 −60
Original line number Diff line number Diff line
@@ -46,13 +46,18 @@ import android.os.storage.IObbActionListener;
import android.os.storage.StorageResultCode;
import android.util.Slog;

import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * MountService implements back-end services for platform storage
@@ -181,6 +186,22 @@ class MountService extends IMountService.Stub

            token.asBinder().unlinkToDeath(this, 0);
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder("ObbState{");
            sb.append("filename=");
            sb.append(filename);
            sb.append(",token=");
            sb.append(token.toString());
            sb.append(",callerUid=");
            sb.append(callerUid);
            sb.append(",mounted=");
            sb.append(mounted);
            sb.append('}');
            return sb.toString();
        }

    }

    // OBB Action Handler
@@ -475,6 +496,34 @@ class MountService extends IMountService.Stub
        } else if (Environment.MEDIA_MOUNTED.equals(state)) {
            mPms.updateExternalMediaStatus(true, false);
        }

        // Remove all OBB mappings and listeners from this path
        synchronized (mObbMounts) {
            final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();

            final Iterator<Entry<String, ObbState>> i = mObbPathToStateMap.entrySet().iterator();
            while (i.hasNext()) {
                final Entry<String, ObbState> obbEntry = i.next();

                // If this entry's source file is in the volume path that got
                // unmounted, remove it because it's no longer valid.
                if (obbEntry.getKey().startsWith(path)) {
                    obbStatesToRemove.add(obbEntry.getValue());
                }
            }

            for (final ObbState obbState : obbStatesToRemove) {
                removeObbState(obbState);

                try {
                    obbState.token.onObbResult(obbState.filename, Environment.MEDIA_UNMOUNTED);
                } catch (RemoteException e) {
                    Slog.i(TAG, "Couldn't send unmount notification for  OBB: "
                            + obbState.filename);
                }
            }
        }

        String oldState = mLegacyState;
        mLegacyState = state;

@@ -1494,9 +1543,15 @@ class MountService extends IMountService.Stub

    public boolean isObbMounted(String filename) {
        synchronized (mObbMounts) {
            return mObbPathToStateMap.containsKey(filename);
            final ObbState obbState = mObbPathToStateMap.get(filename);
            if (obbState != null) {
                synchronized (obbState) {
                    return obbState.mounted;
                }
            }
        }
        return false;
    }

    public void mountObb(String filename, String key, IObbActionListener token) {
        waitForReady();
@@ -1512,7 +1567,12 @@ class MountService extends IMountService.Stub

        synchronized (mObbMounts) {
            if (isObbMounted(filename)) {
                throw new IllegalArgumentException("OBB file is already mounted");
                try {
                    token.onObbResult(filename, Environment.MEDIA_MOUNTED);
                } catch (RemoteException e) {
                    Slog.d(TAG, "Could not send unmount notification for: " + filename);
                }
                return;
            }

            final int callerUid = Binder.getCallingUid();
@@ -1526,7 +1586,7 @@ class MountService extends IMountService.Stub
            Slog.e(TAG, "Failed to link to listener death");
        }

        MountObbAction action = new MountObbAction(obbState, key);
        ObbAction action = new MountObbAction(obbState, key);
        mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));

        if (DEBUG_OBB)
@@ -1544,8 +1604,14 @@ class MountService extends IMountService.Stub

        synchronized (mObbMounts) {
            if (!isObbMounted(filename)) {
                throw new IllegalArgumentException("OBB is not mounted");
                try {
                    token.onObbResult(filename, Environment.MEDIA_UNMOUNTED);
                } catch (RemoteException e) {
                    Slog.d(TAG, "Could not send unmount notification for: " + filename);
                }
                return;
            }

            obbState = mObbPathToStateMap.get(filename);

            if (Binder.getCallingUid() != obbState.callerUid) {
@@ -1555,7 +1621,7 @@ class MountService extends IMountService.Stub
            }
        }

        UnmountObbAction action = new UnmountObbAction(obbState, force);
        ObbAction action = new UnmountObbAction(obbState, force);
        mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));

        if (DEBUG_OBB)
@@ -1587,6 +1653,13 @@ class MountService extends IMountService.Stub
        }
    }

    private void replaceObbState(ObbState oldObbState, ObbState newObbState) {
        synchronized (mObbMounts) {
            removeObbState(oldObbState);
            addObbState(newObbState);
        }
    }

    private class ObbActionHandler extends Handler {
        private boolean mBound = false;
        private List<ObbAction> mActions = new LinkedList<ObbAction>();
@@ -1760,6 +1833,29 @@ class MountService extends IMountService.Stub

        abstract void handleExecute() throws RemoteException, IOException;
        abstract void handleError();

        protected ObbInfo getObbInfo() throws IOException {
            ObbInfo obbInfo;
            try {
                obbInfo = mContainerService.getObbInfo(mObbState.filename);
            } catch (RemoteException e) {
                Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
                        + mObbState.filename);
                obbInfo = null;
            }
            if (obbInfo == null) {
                throw new IOException("Couldn't read OBB file: " + mObbState.filename);
            }
            return obbInfo;
        }

        protected void sendNewStatusOrIgnore(String filename, String status) {
            try {
                mObbState.token.onObbResult(filename, status);
            } catch (RemoteException e) {
                Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
            }
        }
    }

    class MountObbAction extends ObbAction {
@@ -1770,10 +1866,42 @@ class MountService extends IMountService.Stub
            mKey = key;
        }

        public void handleExecute() throws RemoteException, IOException {
            final ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
            if (obbInfo == null) {
                throw new IOException("Couldn't read OBB file: " + mObbState.filename);
        public void handleExecute() throws IOException {
            final ObbInfo obbInfo = getObbInfo();

            /*
             * If someone tried to trick us with some weird characters, rectify
             * it here.
             */
            if (!mObbState.filename.equals(obbInfo.filename)) {
                if (DEBUG_OBB)
                    Slog.i(TAG, "OBB filename " + mObbState.filename + " is actually "
                            + obbInfo.filename);

                synchronized (mObbMounts) {
                    /*
                     * If the real filename is already mounted, discard this
                     * state and notify the caller that the OBB is already
                     * mounted.
                     */
                    if (isObbMounted(obbInfo.filename)) {
                        if (DEBUG_OBB)
                            Slog.i(TAG, "OBB already mounted as " + obbInfo.filename);

                        removeObbState(mObbState);
                        sendNewStatusOrIgnore(obbInfo.filename, Environment.MEDIA_MOUNTED);
                        return;
                    }

                    /*
                     * It's not already mounted, so we have to replace the state
                     * with the state containing the actual filename.
                     */
                    ObbState newObbState = new ObbState(obbInfo.filename, mObbState.token,
                            mObbState.callerUid);
                    replaceObbState(mObbState, newObbState);
                    mObbState = newObbState;
                }
            }

            if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
@@ -1784,7 +1912,15 @@ class MountService extends IMountService.Stub
                mKey = "none";
            }

            int rc = StorageResultCode.OperationSucceeded;
            boolean mounted = false;
            int rc;
            synchronized (mObbState) {
                if (mObbState.mounted) {
                    sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
                    return;
                }

                rc = StorageResultCode.OperationSucceeded;
                String cmd = String.format("obb mount %s %s %d", mObbState.filename, mKey,
                        mObbState.callerUid);
                try {
@@ -1797,29 +1933,26 @@ class MountService extends IMountService.Stub
                }

                if (rc == StorageResultCode.OperationSucceeded) {
                try {
                    mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_MOUNTED);
                } catch (RemoteException e) {
                    Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
                    mObbState.mounted = mounted = true;
                }
            }

            if (mounted) {
                sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
            } else {
                Slog.e(TAG, "Couldn't mount OBB file: " + rc);

                // We didn't succeed, so remove this from the mount-set.
                removeObbState(mObbState);

                mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
                sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
            }
        }

        public void handleError() {
            removeObbState(mObbState);

            try {
                mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
            } catch (RemoteException e) {
                Slog.e(TAG, "Couldn't send back OBB mount error for " + mObbState.filename);
            }
            sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
        }

        @Override
@@ -1845,10 +1978,31 @@ class MountService extends IMountService.Stub
            mForceUnmount = force;
        }

        public void handleExecute() throws RemoteException, IOException {
            final ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
            if (obbInfo == null) {
                throw new IOException("Couldn't read OBB file: " + mObbState.filename);
        public void handleExecute() throws IOException {
            final ObbInfo obbInfo = getObbInfo();

            /*
             * If someone tried to trick us with some weird characters, rectify
             * it here.
             */
            synchronized (mObbMounts) {
                if (!isObbMounted(obbInfo.filename)) {
                    removeObbState(mObbState);
                    sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
                    return;
                }

                if (!mObbState.filename.equals(obbInfo.filename)) {
                    removeObbState(mObbState);
                    mObbState = mObbPathToStateMap.get(obbInfo.filename);
                }
            }

            boolean unmounted = false;
            synchronized (mObbState) {
                if (!mObbState.mounted) {
                    sendNewStatusOrIgnore(obbInfo.filename, Environment.MEDIA_UNMOUNTED);
                    return;
                }

                int rc = StorageResultCode.OperationSucceeded;
@@ -1866,30 +2020,24 @@ class MountService extends IMountService.Stub
                }

                if (rc == StorageResultCode.OperationSucceeded) {
                    mObbState.mounted = false;
                    unmounted = true;
                }
            }

            if (unmounted) {
                removeObbState(mObbState);

                try {
                    mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_UNMOUNTED);
                } catch (RemoteException e) {
                    Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
                }
                sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
            } else {
                try {
                    mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
                } catch (RemoteException e) {
                    Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
                }
                sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
            }
        }

        public void handleError() {
            removeObbState(mObbState);

            try {
                mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
            } catch (RemoteException e) {
                Slog.e(TAG, "Couldn't send back OBB unmount error for " + mObbState.filename);
            }
            sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
        }

        @Override
@@ -1908,5 +2056,27 @@ class MountService extends IMountService.Stub
            return sb.toString();
        }
    }

    @Override
    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
            pw.println("Permission Denial: can't dump ActivityManager from from pid="
                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
                    + " without permission " + android.Manifest.permission.DUMP);
            return;
        }

        pw.println("  mObbMounts:");

        synchronized (mObbMounts) {
            final Collection<List<ObbState>> obbStateLists = mObbMounts.values();

            for (final List<ObbState> obbStates : obbStateLists) {
                for (final ObbState obbState : obbStates) {
                    pw.print("    "); pw.println(obbState.toString());
                }
            }
        }
    }
}