// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.util;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.TransactionTooLargeException;
import android.support.v4.app.BundleCompat;
import org.chromium.base.Log;
import org.chromium.base.VisibleForTesting;
import java.util.ArrayList;
/**
* Utilities dealing with extracting information from intents.
*/
public class IntentUtils {
private static final String TAG = "IntentUtils";
/** See {@link #isIntentTooLarge(Intent)}. */
private static final int MAX_INTENT_SIZE_THRESHOLD = 750000;
/**
* Just like {@link Intent#hasExtra(String)} but doesn't throw exceptions.
*/
public static boolean safeHasExtra(Intent intent, String name) {
try {
return intent.hasExtra(name);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "hasExtra failed on intent " + intent);
return false;
}
}
/**
* Just like {@link Intent#removeExtra(String)} but doesn't throw exceptions.
*/
public static void safeRemoveExtra(Intent intent, String name) {
try {
intent.removeExtra(name);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "removeExtra failed on intent " + intent);
}
}
/**
* Just like {@link Intent#getBooleanExtra(String, boolean)} but doesn't throw exceptions.
*/
public static boolean safeGetBooleanExtra(Intent intent, String name, boolean defaultValue) {
try {
return intent.getBooleanExtra(name, defaultValue);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "getBooleanExtra failed on intent " + intent);
return defaultValue;
}
}
/**
* Just like {@link Intent#getIntExtra(String, int)} but doesn't throw exceptions.
*/
public static int safeGetIntExtra(Intent intent, String name, int defaultValue) {
try {
return intent.getIntExtra(name, defaultValue);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "getIntExtra failed on intent " + intent);
return defaultValue;
}
}
/**
* Just like {@link Bundle#getInt(String, int)} but doesn't throw exceptions.
*/
public static int safeGetInt(Bundle bundle, String name, int defaultValue) {
try {
return bundle.getInt(name, defaultValue);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "getInt failed on bundle " + bundle);
return defaultValue;
}
}
/**
* Just like {@link Intent#getIntArrayExtra(String)} but doesn't throw exceptions.
*/
public static int[] safeGetIntArrayExtra(Intent intent, String name) {
try {
return intent.getIntArrayExtra(name);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "getIntArrayExtra failed on intent " + intent);
return null;
}
}
/**
* Just like {@link Bundle#getIntArray(String)} but doesn't throw exceptions.
*/
public static int[] safeGetIntArray(Bundle bundle, String name) {
try {
return bundle.getIntArray(name);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "getIntArray failed on bundle " + bundle);
return null;
}
}
/**
* Just like {@link Intent#getLongExtra(String, long)} but doesn't throw exceptions.
*/
public static long safeGetLongExtra(Intent intent, String name, long defaultValue) {
try {
return intent.getLongExtra(name, defaultValue);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "getLongExtra failed on intent " + intent);
return defaultValue;
}
}
/**
* Just like {@link Intent#getStringExtra(String)} but doesn't throw exceptions.
*/
public static String safeGetStringExtra(Intent intent, String name) {
try {
return intent.getStringExtra(name);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "getStringExtra failed on intent " + intent);
return null;
}
}
/**
* Just like {@link Bundle#getString(String)} but doesn't throw exceptions.
*/
public static String safeGetString(Bundle bundle, String name) {
try {
return bundle.getString(name);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "getString failed on bundle " + bundle);
return null;
}
}
/**
* Just like {@link Intent#getBundleExtra(String)} but doesn't throw exceptions.
*/
public static Bundle safeGetBundleExtra(Intent intent, String name) {
try {
return intent.getBundleExtra(name);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "getBundleExtra failed on intent " + intent);
return null;
}
}
/**
* Just like {@link Bundle#getBundle(String)} but doesn't throw exceptions.
*/
public static Bundle safeGetBundle(Bundle bundle, String name) {
try {
return bundle.getBundle(name);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "getBundle failed on bundle " + bundle);
return null;
}
}
/**
* Just like {@link Bundle#getParcelable(String)} but doesn't throw exceptions.
*/
public static <T extends Parcelable> T safeGetParcelable(Bundle bundle, String name) {
try {
return bundle.getParcelable(name);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "getParcelable failed on bundle " + bundle);
return null;
}
}
/**
* Just like {@link Intent#getParcelableExtra(String)} but doesn't throw exceptions.
*/
public static <T extends Parcelable> T safeGetParcelableExtra(Intent intent, String name) {
try {
return intent.getParcelableExtra(name);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "getParcelableExtra failed on intent " + intent);
return null;
}
}
/**
* Just link {@link Intent#getParcelableArrayListExtra(String)} but doesn't throw exceptions.
*/
public static <T extends Parcelable> ArrayList<T> getParcelableArrayListExtra(
Intent intent, String name) {
try {
return intent.getParcelableArrayListExtra(name);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "getParcelableArrayListExtra failed on intent " + intent);
return null;
}
}
/**
* Just like {@link Intent#getParcelableArrayExtra(String)} but doesn't throw exceptions.
*/
public static Parcelable[] safeGetParcelableArrayExtra(Intent intent, String name) {
try {
return intent.getParcelableArrayExtra(name);
} catch (Throwable t) {
Log.e(TAG, "getParcelableArrayExtra failed on intent " + intent);
return null;
}
}
/**
* Just like {@link Intent#getStringArrayListExtra(String)} but doesn't throw exceptions.
*/
public static ArrayList<String> safeGetStringArrayListExtra(Intent intent, String name) {
try {
return intent.getStringArrayListExtra(name);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "getStringArrayListExtra failed on intent " + intent);
return null;
}
}
/**
* Just like {@link Intent#getByteArrayExtra(String)} but doesn't throw exceptions.
*/
public static byte[] safeGetByteArrayExtra(Intent intent, String name) {
try {
return intent.getByteArrayExtra(name);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "getByteArrayExtra failed on intent " + intent);
return null;
}
}
/**
* Just like {@link BundleCompat#getBinder()}, but doesn't throw exceptions.
*/
public static IBinder safeGetBinder(Bundle bundle, String name) {
if (bundle == null) return null;
try {
return BundleCompat.getBinder(bundle, name);
} catch (Throwable t) {
// Catches un-parceling exceptions.
Log.e(TAG, "getBinder failed on bundle " + bundle);
return null;
}
}
/**
* @return a Binder from an Intent, or null.
*
* Creates a temporary copy of the extra Bundle, which is required as
* Intent#getBinderExtra() doesn't exist, but Bundle.getBinder() does.
*/
@VisibleForTesting
public static IBinder safeGetBinderExtra(Intent intent, String name) {
if (!intent.hasExtra(name)) return null;
Bundle extras = intent.getExtras();
return safeGetBinder(extras, name);
}
/**
* Inserts a {@link Binder} value into an Intent as an extra.
*
* Uses {@link BundleCompat#putBinder()}, but doesn't throw exceptions.
*
* @param intent Intent to put the binder into.
* @param name Key.
* @param binder Binder object.
*/
@VisibleForTesting
public static void safePutBinderExtra(Intent intent, String name, IBinder binder) {
if (intent == null) return;
Bundle bundle = new Bundle();
try {
BundleCompat.putBinder(bundle, name, binder);
} catch (Throwable t) {
// Catches parceling exceptions.
Log.e(TAG, "putBinder failed on bundle " + bundle);
}
intent.putExtras(bundle);
}
/**
* Catches any failures to start an Activity.
* @param context Context to use when starting the Activity.
* @param intent Intent to fire.
* @return Whether or not Android accepted the Intent.
*/
public static boolean safeStartActivity(Context context, Intent intent) {
try {
context.startActivity(intent);
return true;
} catch (ActivityNotFoundException e) {
return false;
}
}
/**
* Returns how large the Intent will be in Parcel form, which is helpful for gauging whether
* Android will deliver the Intent instead of throwing a TransactionTooLargeException.
*
* @param intent Intent to get the size of.
* @return Number of bytes required to parcel the Intent.
*/
public static int getParceledIntentSize(Intent intent) {
Parcel parcel = Parcel.obtain();
intent.writeToParcel(parcel, 0);
return parcel.dataSize();
}
/**
* Determines if an Intent's size is bigger than a reasonable threshold. Having too many large
* transactions in flight simultaneously (including Intents) causes Android to throw a
* {@link TransactionTooLargeException}. According to that class, the limit across all
* transactions combined is one megabyte. Best practice is to keep each individual Intent well
* under the limit to avoid this situation.
*/
public static boolean isIntentTooLarge(Intent intent) {
return getParceledIntentSize(intent) > MAX_INTENT_SIZE_THRESHOLD;
}
}