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

Commit 364ed4dd authored by Amith Yamasani's avatar Amith Yamasani
Browse files

PreferenceActivity Fragment security

Provides a way for PreferenceActivities to verify that they
aren't being launched with Fragments not meant to be attached to
them. Default implementation will fail verification for apps built
with KLP. This will not affect apps built for earlier targets.

Also, make sure that the class being instantiated is a Fragment and
not some random class.

Bug: 9901133

Change-Id: I564cd5168eabcadd7594ea8011e2081ebebfe063
parent cb9accef
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -18351,6 +18351,7 @@ package android.preference {
    method public boolean hasHeaders();
    method public void invalidateHeaders();
    method public boolean isMultiPane();
    method protected boolean isValidFragment(java.lang.String);
    method public void loadHeadersFromResource(int, java.util.List<android.preference.PreferenceActivity.Header>);
    method public void onBuildHeaders(java.util.List<android.preference.PreferenceActivity.Header>);
    method public android.content.Intent onBuildStartFragmentIntent(java.lang.String, android.os.Bundle, int, int);
+4 −0
Original line number Diff line number Diff line
@@ -580,6 +580,10 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
            if (clazz == null) {
                // Class not found in the cache, see if it's real, and try to add it
                clazz = context.getClassLoader().loadClass(fname);
                if (!Fragment.class.isAssignableFrom(clazz)) {
                    throw new InstantiationException("Trying to instantiate a class " + fname
                            + " that is not a Fragment", new ClassCastException());
                }
                sClassMap.put(fname, clazz);
            }
            Fragment f = (Fragment)clazz.newInstance();
+36 −2
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.util.Xml;
import android.view.LayoutInflater;
@@ -124,6 +125,8 @@ public abstract class PreferenceActivity extends ListActivity implements
        PreferenceManager.OnPreferenceTreeClickListener,
        PreferenceFragment.OnPreferenceStartFragmentCallback {

    private static final String TAG = "PreferenceActivity";

    // Constants for state save/restore
    private static final String HEADERS_TAG = ":android:headers";
    private static final String CUR_HEADER_TAG = ":android:cur_header";
@@ -132,6 +135,9 @@ public abstract class PreferenceActivity extends ListActivity implements
    /**
     * When starting this activity, the invoking Intent can contain this extra
     * string to specify which fragment should be initially displayed.
     * <p/>Starting from Key Lime Pie, when this argument is passed in, the PreferenceActivity
     * will call isValidFragment() to confirm that the fragment class name is valid for this
     * activity.
     */
    public static final String EXTRA_SHOW_FRAGMENT = ":android:show_fragment";

@@ -877,7 +883,27 @@ public abstract class PreferenceActivity extends ListActivity implements
        } finally {
            if (parser != null) parser.close();
        }
    }

    /**
     * Subclasses should override this method and verify that the given fragment is a valid type
     * to be attached to this activity. The default implementation returns <code>true</code> prior
     * to Key Lime Pie, <code>false</code> otherwise.
     * @param f the class name of the Fragment about to be attached to this activity.
     * @return true if the fragment class name is valid for this Activity and false otherwise.
     */
    protected boolean isValidFragment(String fragmentName) {
        if (getApplicationInfo().targetSdkVersion  >= android.os.Build.VERSION_CODES.KEY_LIME_PIE) {
            Log.w(TAG, "Subclasses of PreferenceActivity must override isValidFragment(String)"
                    + " to verify that the Fragment class is valid! " + this.getClass().getName()
                    + " has not checked if fragment " + fragmentName + " is valid.");
            // Return true for now, but will eventually return false when all bundled apps
            // have been modified. TODO: change to return false
            return true;
        } else {
            Log.i(TAG, "PreferenceActivity built on pre-KLP launching fragment: " + fragmentName);
            return true;
        }
    }

    /**
@@ -1146,6 +1172,10 @@ public abstract class PreferenceActivity extends ListActivity implements
    private void switchToHeaderInner(String fragmentName, Bundle args, int direction) {
        getFragmentManager().popBackStack(BACK_STACK_PREFS,
                FragmentManager.POP_BACK_STACK_INCLUSIVE);
        if (!isValidFragment(fragmentName)) {
            throw new IllegalArgumentException("Invalid fragment for this activity: "
                    + fragmentName);
        }
        Fragment f = Fragment.instantiate(this, fragmentName, args);
        FragmentTransaction transaction = getFragmentManager().beginTransaction();
        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
@@ -1275,6 +1305,10 @@ public abstract class PreferenceActivity extends ListActivity implements
        if (mSinglePane) {
            startWithFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, 0);
        } else {
            if (!isValidFragment(fragmentClass)) {
                throw new IllegalArgumentException("Invalid fragment for this activity: "
                        + fragmentClass);
            }
            Fragment f = Fragment.instantiate(this, fragmentClass, args);
            if (resultTo != null) {
                f.setTargetFragment(resultTo, resultRequestCode);