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

Commit ef73ee1d authored by Craig Mautner's avatar Craig Mautner
Browse files

Add code for persisting tasks and activities to disk

Recent tasks that have the persistable flag set are
saved to /data/system/recent_tasks/ on shutdown and in the
background. Their thumbnails are saved to
/data/system/recent_images/.

Change-Id: Ifb820a01c412fe1f8c0f6e41aa655fafd89eaa8d
parent e4ca3050
Loading
Loading
Loading
Loading
+83 −3
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import android.util.AttributeSet;
import android.util.Log;

import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlSerializer;

import java.io.IOException;
import java.io.Serializable;
@@ -604,6 +605,15 @@ import java.util.Set;
 * of all possible flags.
 */
public class Intent implements Parcelable, Cloneable {
    private static final String ATTR_ACTION = "action";
    private static final String TAG_CATEGORIES = "categories";
    private static final String ATTR_CATEGORY = "category";
    private static final String TAG_EXTRA = "extra";
    private static final String ATTR_TYPE = "type";
    private static final String ATTR_COMPONENT = "component";
    private static final String ATTR_DATA = "data";
    private static final String ATTR_FLAGS = "flags";

    // ---------------------------------------------------------------------
    // ---------------------------------------------------------------------
    // Standard intent activity actions (see action variable).
@@ -7348,7 +7358,7 @@ public class Intent implements Parcelable, Cloneable {
            }

            String nodeName = parser.getName();
            if (nodeName.equals("category")) {
            if (nodeName.equals(TAG_CATEGORIES)) {
                sa = resources.obtainAttributes(attrs,
                        com.android.internal.R.styleable.IntentCategory);
                String cat = sa.getString(com.android.internal.R.styleable.IntentCategory_name);
@@ -7359,11 +7369,11 @@ public class Intent implements Parcelable, Cloneable {
                }
                XmlUtils.skipCurrentTag(parser);

            } else if (nodeName.equals("extra")) {
            } else if (nodeName.equals(TAG_EXTRA)) {
                if (intent.mExtras == null) {
                    intent.mExtras = new Bundle();
                }
                resources.parseBundleExtra("extra", attrs, intent.mExtras);
                resources.parseBundleExtra(TAG_EXTRA, attrs, intent.mExtras);
                XmlUtils.skipCurrentTag(parser);

            } else {
@@ -7374,6 +7384,76 @@ public class Intent implements Parcelable, Cloneable {
        return intent;
    }

    /** @hide */
    public void saveToXml(XmlSerializer out) throws IOException {
        if (mAction != null) {
            out.attribute(null, ATTR_ACTION, mAction);
        }
        if (mData != null) {
            out.attribute(null, ATTR_DATA, mData.toString());
        }
        if (mType != null) {
            out.attribute(null, ATTR_TYPE, mType);
        }
        if (mComponent != null) {
            out.attribute(null, ATTR_COMPONENT, mComponent.flattenToShortString());
        }
        out.attribute(null, ATTR_FLAGS, Integer.toHexString(getFlags()));

        if (mCategories != null) {
            out.startTag(null, TAG_CATEGORIES);
            for (int categoryNdx = mCategories.size() - 1; categoryNdx >= 0; --categoryNdx) {
                out.attribute(null, ATTR_CATEGORY, mCategories.valueAt(categoryNdx));
            }
        }
    }

    /** @hide */
    public static Intent restoreFromXml(XmlPullParser in) throws IOException,
            XmlPullParserException {
        Intent intent = new Intent();
        final int outerDepth = in.getDepth();

        int attrCount = in.getAttributeCount();
        for (int attrNdx = attrCount - 1; attrNdx >= 0; --attrNdx) {
            final String attrName = in.getAttributeName(attrNdx);
            final String attrValue = in.getAttributeValue(attrNdx);
            if (ATTR_ACTION.equals(attrName)) {
                intent.setAction(attrValue);
            } else if (ATTR_DATA.equals(attrName)) {
                intent.setData(Uri.parse(attrValue));
            } else if (ATTR_TYPE.equals(attrName)) {
                intent.setType(attrValue);
            } else if (ATTR_COMPONENT.equals(attrName)) {
                intent.setComponent(ComponentName.unflattenFromString(attrValue));
            } else if (ATTR_FLAGS.equals(attrName)) {
                intent.setFlags(Integer.valueOf(attrValue, 16));
            } else {
                Log.e("Intent", "restoreFromXml: unknown attribute=" + attrName);
            }
        }

        int event;
        String name;
        while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
                (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
            if (event == XmlPullParser.START_TAG) {
                name = in.getName();
                if (TAG_CATEGORIES.equals(name)) {
                    attrCount = in.getAttributeCount();
                    for (int attrNdx = attrCount - 1; attrNdx >= 0; --attrNdx) {
                        intent.addCategory(in.getAttributeValue(attrNdx));
                    }
                } else {
                    Log.w("Intent", "restoreFromXml: unknown name=" + name);
                    XmlUtils.skipCurrentTag(in);
                }
            }
        }

        return intent;
    }

    /**
     * Normalize a MIME data type.
     *
+11 −2
Original line number Diff line number Diff line
@@ -18,11 +18,10 @@ package android.os;

import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
@@ -303,6 +302,16 @@ abstract class CommonBundle implements Parcelable, Cloneable {
        mMap.putAll(bundle.mMap);
    }

    /**
     * Inserts all mappings from the given Map into this CommonBundle.
     *
     * @param map a Map
     */
    void putAll(Map map) {
        unparcel();
        mMap.putAll(map);
    }

    /**
     * Returns a Set containing the Strings used as keys in this Bundle.
     *
+95 −2
Original line number Diff line number Diff line
@@ -17,7 +17,14 @@
package android.os;

import android.util.ArrayMap;

import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
@@ -25,7 +32,8 @@ import java.util.Set;
 * restored.
 *
 */
public final class PersistableBundle extends CommonBundle {
public final class PersistableBundle extends CommonBundle implements XmlUtils.WriteMapCallback {
    private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
    public static final PersistableBundle EMPTY;
    static final Parcel EMPTY_PARCEL;

@@ -87,6 +95,38 @@ public final class PersistableBundle extends CommonBundle {
        super(b);
    }

    /**
     * Constructs a PersistableBundle containing the mappings passed in.
     *
     * @param map a Map containing only those items that can be persisted.
     * @throws IllegalArgumentException if any element of #map cannot be persisted.
     */
    private PersistableBundle(Map<String, Object> map) {
        super();

        // First stuff everything in.
        putAll(map);

        // Now verify each item throwing an exception if there is a violation.
        Set<String> keys = map.keySet();
        Iterator<String> iterator = keys.iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            Object value = map.get(key);
            if (value instanceof Map) {
                // Fix up any Maps by replacing them with PersistableBundles.
                putPersistableBundle(key, new PersistableBundle((Map<String, Object>) value));
            } else if (!(value instanceof Integer) && !(value instanceof Long) &&
                    !(value instanceof Double) && !(value instanceof String) &&
                    !(value instanceof int[]) && !(value instanceof long[]) &&
                    !(value instanceof double[]) && !(value instanceof String[]) &&
                    !(value instanceof PersistableBundle) && (value != null)) {
                throw new IllegalArgumentException("Bad value in PersistableBundle key=" + key +
                        " value=" + value);
            }
        }
    }

    /**
     * Make a PersistableBundle for a single key/value pair.
     *
@@ -206,6 +246,7 @@ public final class PersistableBundle extends CommonBundle {
     *
     * @param bundle a PersistableBundle
     */
    @Override
    public void putAll(PersistableBundle bundle) {
        super.putAll(bundle);
    }
@@ -323,6 +364,7 @@ public final class PersistableBundle extends CommonBundle {
     * @param key a String, or null
     * @param value a Bundle object, or null
     */
    @Override
    public void putPersistableBundle(String key, PersistableBundle value) {
        super.putPersistableBundle(key, value);
    }
@@ -539,6 +581,57 @@ public final class PersistableBundle extends CommonBundle {
        super.readFromParcelInner(parcel);
    }

    /** @hide */
    @Override
    public void writeUnknownObject(Object v, String name, XmlSerializer out)
            throws XmlPullParserException, IOException {
        if (v instanceof PersistableBundle) {
            out.startTag(null, TAG_PERSISTABLEMAP);
            out.attribute(null, "name", name);
            ((PersistableBundle) v).saveToXml(out);
            out.endTag(null, TAG_PERSISTABLEMAP);
        } else {
            throw new XmlPullParserException("Unknown Object o=" + v);
        }
    }

    /** @hide */
    public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
        unparcel();
        XmlUtils.writeMapXml(mMap, out, this);
    }

    /** @hide */
    static class MyReadMapCallback implements  XmlUtils.ReadMapCallback {
        @Override
        public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
                throws XmlPullParserException, IOException {
            if (TAG_PERSISTABLEMAP.equals(tag)) {
                return restoreFromXml(in);
            }
            throw new XmlPullParserException("Unknown tag=" + tag);
        }
    }

    /**
     * @hide
     */
    public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
            XmlPullParserException {
        final int outerDepth = in.getDepth();
        final String startTag = in.getName();
        final String[] tagName = new String[1];
        int event;
        while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
                (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
            if (event == XmlPullParser.START_TAG) {
                return new PersistableBundle((Map<String, Object>)
                        XmlUtils.readThisMapXml(in, startTag, tagName, new MyReadMapCallback()));
            }
        }
        return EMPTY;
    }

    @Override
    synchronized public String toString() {
        if (mParcelledData != null) {
+521 −29

File changed.

Preview size limit exceeded, changes collapsed.

+26 −9
Original line number Diff line number Diff line
@@ -408,7 +408,7 @@ public final class ActivityManagerService extends ActivityManagerNative
    /**
     * List of intents that were used to start the most recent tasks.
     */
    final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>();
    ArrayList<TaskRecord> mRecentTasks;
    public class PendingAssistExtras extends Binder implements Runnable {
        public final ActivityRecord activity;
@@ -820,6 +820,11 @@ public final class ActivityManagerService extends ActivityManagerNative
     */
    final AppOpsService mAppOpsService;
    /**
     * Save recent tasks information across reboots.
     */
    final TaskPersister mTaskPersister;
    /**
     * Current configuration information.  HistoryRecord objects are given
     * a reference to this object to indicate which configuration they are
@@ -2137,6 +2142,7 @@ public final class ActivityManagerService extends ActivityManagerNative
        mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
        mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
        mStackSupervisor = new ActivityStackSupervisor(this);
        mTaskPersister = new TaskPersister(systemDir, mStackSupervisor);
        mProcessCpuThread = new Thread("CpuTracker") {
            @Override
@@ -7080,12 +7086,12 @@ public final class ActivityManagerService extends ActivityManagerNative
    private ActivityManager.RecentTaskInfo createRecentTaskInfoFromTaskRecord(TaskRecord tr) {
        ActivityManager.RecentTaskInfo rti
                = new ActivityManager.RecentTaskInfo();
        rti.id = tr.numActivities > 0 ? tr.taskId : -1;
        rti.id = tr.mActivities.isEmpty() ? -1 : tr.taskId;
        rti.persistentId = tr.taskId;
        rti.baseIntent = new Intent(tr.getBaseIntent());
        rti.origActivity = tr.origActivity;
        rti.description = tr.lastDescription;
        rti.stackId = tr.stack.mStackId;
        rti.stackId = tr.stack != null ? tr.stack.mStackId : -1;
        rti.userId = tr.userId;
        rti.taskDescription = new ActivityManager.TaskDescription(tr.lastTaskDescription);
        return rti;
@@ -7319,6 +7325,9 @@ public final class ActivityManagerService extends ActivityManagerNative
        if (tr != null) {
            tr.removeTaskActivitiesLocked(-1, false);
            cleanUpRemovedTaskLocked(tr, flags);
            if (tr.isPersistable) {
                notifyTaskPersisterLocked(tr, true);
            }
            return true;
        }
        return false;
@@ -7558,14 +7567,11 @@ public final class ActivityManagerService extends ActivityManagerNative
        try {
            synchronized (this) {
                TaskRecord tr = recentTaskForIdLocked(taskId);
                if (tr != null) {
                    return tr.stack.isHomeStack();
                }
                return tr != null && tr.stack != null && tr.stack.isHomeStack();
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        return false;
    }
    @Override
@@ -8627,6 +8633,10 @@ public final class ActivityManagerService extends ActivityManagerNative
        }
    }
    void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
        mTaskPersister.notify(task, flush);
    }
    @Override
    public boolean shutdown(int timeout) {
        if (checkCallingPermission(android.Manifest.permission.SHUTDOWN)
@@ -8649,6 +8659,7 @@ public final class ActivityManagerService extends ActivityManagerNative
        synchronized (this) {
            mProcessStats.shutdownLocked();
        }
        notifyTaskPersisterLocked(null, true);
        return timedout;
    }
@@ -9555,6 +9566,12 @@ public final class ActivityManagerService extends ActivityManagerNative
                return;
            }
            mRecentTasks = mTaskPersister.restoreTasksLocked();
            if (!mRecentTasks.isEmpty()) {
                mStackSupervisor.createStackForRestoredTaskHistory(mRecentTasks);
            }
            mTaskPersister.startPersisting();
            // Check to see if there are any update receivers to run.
            if (!mDidUpdate) {
                if (mWaitingUpdate) {
@@ -17171,7 +17188,7 @@ public final class ActivityManagerService extends ActivityManagerNative
    /**
     * An implementation of IAppTask, that allows an app to manage its own tasks via
     * {@link android.app.ActivityManager#AppTask}.  We keep track of the callingUid to ensure that
     * {@link android.app.ActivityManager.AppTask}.  We keep track of the callingUid to ensure that
     * only the process that calls getAppTasks() can call the AppTask methods.
     */
    class AppTaskImpl extends IAppTask.Stub {
Loading