Exposing file:// beyond your app is bad, m'kay?
For several releases now we've told developers that sharing raw files
between apps is a recipe for trouble. There are at least three major
problems with sending raw files:
-- Apps sending generic intents can't know who is at the other end,
so they may not have access to shared storage locations. This is
more likely now that runtime permissions require apps to explicitly
ask users for permission.
-- Apps making files in their private storage world-readable has been
deprecated for several releases, and now in N it's fully blocked. If
we let these intents through, the receiving app would fail to open
the file, when the real blame rests on the sending app.
-- Devices with user profiles can't share raw files when using
cross-profile intent filters, since filesystem access is fully
locked down between users.
The time has finally come to communicate clearly that if you're
sharing content between apps, you need to use content:// Uris. We
added the simple FileProvider several years ago to give apps a clean
way to migrate with minimal work on their part.
Bug: 26860922, 9069185
Change-Id: I075f627f6a0d6c7fca2c090ca133b9aae9801c64
diff --git a/api/current.txt b/api/current.txt
index f13601e..64b5d7d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28536,6 +28536,10 @@
field public static final int OPEN = 32; // 0x20
}
+ public class FileUriExposedException extends java.lang.RuntimeException {
+ ctor public FileUriExposedException(java.lang.String);
+ }
+
public class Handler {
ctor public Handler();
ctor public Handler(android.os.Handler.Callback);
@@ -29127,6 +29131,7 @@
method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
+ method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 2fc93fa..5e69084 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -30421,6 +30421,10 @@
field public static final int OPEN = 32; // 0x20
}
+ public class FileUriExposedException extends java.lang.RuntimeException {
+ ctor public FileUriExposedException(java.lang.String);
+ }
+
public class Handler {
ctor public Handler();
ctor public Handler(android.os.Handler.Callback);
@@ -31020,6 +31024,7 @@
method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
+ method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 48fd6f0..ad06e8e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -28545,6 +28545,10 @@
field public static final int OPEN = 32; // 0x20
}
+ public class FileUriExposedException extends java.lang.RuntimeException {
+ ctor public FileUriExposedException(java.lang.String);
+ }
+
public class Handler {
ctor public Handler();
ctor public Handler(android.os.Handler.Callback);
@@ -29136,6 +29140,7 @@
method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
+ method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 11154f2..dc3f64a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4397,7 +4397,7 @@
String resolvedType = null;
if (fillInIntent != null) {
fillInIntent.migrateExtraStreamToClipData();
- fillInIntent.prepareToLeaveProcess();
+ fillInIntent.prepareToLeaveProcess(this);
resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver());
}
int result = ActivityManagerNative.getDefault()
@@ -4629,7 +4629,7 @@
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
intent.migrateExtraStreamToClipData();
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
result = ActivityManagerNative.getDefault()
.startActivity(mMainThread.getApplicationThread(), getBasePackageName(),
intent, intent.resolveTypeIfNeeded(getContentResolver()), mToken,
@@ -4700,7 +4700,7 @@
if (mParent == null) {
try {
intent.migrateExtraStreamToClipData();
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
return ActivityManagerNative.getDefault()
.startNextMatchingActivity(mToken, intent, options);
} catch (RemoteException e) {
@@ -5128,7 +5128,7 @@
if (false) Log.v(TAG, "Finishing self: token=" + mToken);
try {
if (resultData != null) {
- resultData.prepareToLeaveProcess();
+ resultData.prepareToLeaveProcess(this);
}
if (ActivityManagerNative.getDefault()
.finishActivity(mToken, resultCode, resultData, finishTask)) {
@@ -5355,7 +5355,7 @@
@PendingIntent.Flags int flags) {
String packageName = getPackageName();
try {
- data.prepareToLeaveProcess();
+ data.prepareToLeaveProcess(this);
IIntentSender target =
ActivityManagerNative.getDefault().getIntentSender(
ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName,
@@ -6335,10 +6335,10 @@
resultData = mResultData;
}
if (resultData != null) {
- resultData.prepareToLeaveProcess();
+ resultData.prepareToLeaveProcess(this);
}
try {
- upIntent.prepareToLeaveProcess();
+ upIntent.prepareToLeaveProcess(this);
return ActivityManagerNative.getDefault().navigateUpTo(mToken, upIntent,
resultCode, resultData);
} catch (RemoteException e) {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1e7457c..100e67b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -56,6 +56,7 @@
import android.opengl.GLUtils;
import android.os.AsyncTask;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.DropBoxManager;
@@ -4959,16 +4960,22 @@
}
/**
- * For apps targetting SDK Honeycomb or later, we don't allow
- * network usage on the main event loop / UI thread.
- *
- * Note to those grepping: this is what ultimately throws
- * NetworkOnMainThreadException ...
+ * For apps targetting Honeycomb or later, we don't allow network usage
+ * on the main event loop / UI thread. This is what ultimately throws
+ * {@link NetworkOnMainThreadException}.
*/
- if (data.appInfo.targetSdkVersion > 9) {
+ if (data.appInfo.targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
StrictMode.enableDeathOnNetwork();
}
+ /**
+ * For apps targetting N or later, we don't allow file:// Uri exposure.
+ * This is what ultimately throws {@link FileUriExposedException}.
+ */
+ if (data.appInfo.targetSdkVersion >= Build.VERSION_CODES.N) {
+ StrictMode.enableDeathOnFileUriExposure();
+ }
+
NetworkSecurityPolicy.getInstance().setCleartextTrafficPermitted(
(data.appInfo.flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC) != 0);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index fab3740..0d6e93d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -752,7 +752,7 @@
String resolvedType = null;
if (fillInIntent != null) {
fillInIntent.migrateExtraStreamToClipData();
- fillInIntent.prepareToLeaveProcess();
+ fillInIntent.prepareToLeaveProcess(this);
resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver());
}
int result = ActivityManagerNative.getDefault()
@@ -773,7 +773,7 @@
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
@@ -790,7 +790,7 @@
String[] receiverPermissions = receiverPermission == null ? null
: new String[] {receiverPermission};
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
@@ -805,7 +805,7 @@
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
@@ -822,7 +822,7 @@
String[] receiverPermissions = receiverPermission == null ? null
: new String[] {receiverPermission};
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
@@ -839,7 +839,7 @@
String[] receiverPermissions = receiverPermission == null ? null
: new String[] {receiverPermission};
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false,
@@ -856,7 +856,7 @@
String[] receiverPermissions = receiverPermission == null ? null
: new String[] {receiverPermission};
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
@@ -919,7 +919,7 @@
String[] receiverPermissions = receiverPermission == null ? null
: new String[] {receiverPermission};
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, rd,
initialCode, initialData, initialExtras, receiverPermissions, appOp,
@@ -933,7 +933,7 @@
public void sendBroadcastAsUser(Intent intent, UserHandle user) {
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(),
intent, resolvedType, null, Activity.RESULT_OK, null, null, null,
AppOpsManager.OP_NONE, null, false, false, user.getIdentifier());
@@ -955,7 +955,7 @@
String[] receiverPermissions = receiverPermission == null ? null
: new String[] {receiverPermission};
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false,
@@ -1006,7 +1006,7 @@
String[] receiverPermissions = receiverPermission == null ? null
: new String[] {receiverPermission};
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, rd,
initialCode, initialData, initialExtras, receiverPermissions,
@@ -1022,7 +1022,7 @@
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
@@ -1058,7 +1058,7 @@
}
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, rd,
initialCode, initialData, initialExtras, null,
@@ -1077,7 +1077,7 @@
intent.setDataAndType(intent.getData(), resolvedType);
}
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().unbroadcastIntent(
mMainThread.getApplicationThread(), intent, getUserId());
} catch (RemoteException e) {
@@ -1090,7 +1090,7 @@
public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
@@ -1105,7 +1105,7 @@
public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options, false, true,
@@ -1140,7 +1140,7 @@
}
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, rd,
initialCode, initialData, initialExtras, null,
@@ -1159,7 +1159,7 @@
intent.setDataAndType(intent.getData(), resolvedType);
}
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().unbroadcastIntent(
mMainThread.getApplicationThread(), intent, user.getIdentifier());
} catch (RemoteException e) {
@@ -1262,7 +1262,7 @@
private ComponentName startServiceCommon(Intent service, UserHandle user) {
try {
validateServiceIntent(service);
- service.prepareToLeaveProcess();
+ service.prepareToLeaveProcess(this);
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), getOpPackageName(), user.getIdentifier());
@@ -1291,7 +1291,7 @@
private boolean stopServiceCommon(Intent service, UserHandle user) {
try {
validateServiceIntent(service);
- service.prepareToLeaveProcess();
+ service.prepareToLeaveProcess(this);
int res = ActivityManagerNative.getDefault().stopService(
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
@@ -1339,7 +1339,7 @@
< android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
flags |= BIND_WAIVE_PRIORITY;
}
- service.prepareToLeaveProcess();
+ service.prepareToLeaveProcess(this);
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 24a3470..53974c1 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1503,7 +1503,7 @@
}
try {
intent.migrateExtraStreamToClipData();
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(who);
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
@@ -1561,7 +1561,7 @@
String[] resolvedTypes = new String[intents.length];
for (int i=0; i<intents.length; i++) {
intents[i].migrateExtraStreamToClipData();
- intents[i].prepareToLeaveProcess();
+ intents[i].prepareToLeaveProcess(who);
resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver());
}
int result = ActivityManagerNative.getDefault()
@@ -1622,7 +1622,7 @@
}
try {
intent.migrateExtraStreamToClipData();
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(who);
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
@@ -1682,7 +1682,7 @@
}
try {
intent.migrateExtraStreamToClipData();
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(who);
int result = ActivityManagerNative.getDefault()
.startActivityAsUser(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
@@ -1721,7 +1721,7 @@
}
try {
intent.migrateExtraStreamToClipData();
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(who);
int result = ActivityManagerNative.getDefault()
.startActivityAsCaller(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
@@ -1759,7 +1759,7 @@
}
try {
intent.migrateExtraStreamToClipData();
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(who);
int result = appTask.startActivity(whoThread.asBinder(), who.getBasePackageName(),
intent, intent.resolveTypeIfNeeded(who.getContentResolver()), options);
checkStartActivityResult(result, intent);
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index edafe59..412b098 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -307,7 +307,7 @@
context.getContentResolver()) : null;
try {
intent.migrateExtraStreamToClipData();
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(context);
IIntentSender target =
ActivityManagerNative.getDefault().getIntentSender(
ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
@@ -332,7 +332,7 @@
context.getContentResolver()) : null;
try {
intent.migrateExtraStreamToClipData();
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(context);
IIntentSender target =
ActivityManagerNative.getDefault().getIntentSender(
ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
@@ -446,7 +446,7 @@
String[] resolvedTypes = new String[intents.length];
for (int i=0; i<intents.length; i++) {
intents[i].migrateExtraStreamToClipData();
- intents[i].prepareToLeaveProcess();
+ intents[i].prepareToLeaveProcess(context);
resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
}
try {
@@ -472,7 +472,7 @@
String[] resolvedTypes = new String[intents.length];
for (int i=0; i<intents.length; i++) {
intents[i].migrateExtraStreamToClipData();
- intents[i].prepareToLeaveProcess();
+ intents[i].prepareToLeaveProcess(context);
resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
}
try {
@@ -527,7 +527,7 @@
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(context);
IIntentSender target =
ActivityManagerNative.getDefault().getIntentSender(
ActivityManager.INTENT_SENDER_BROADCAST, packageName,
@@ -570,7 +570,7 @@
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
try {
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(context);
IIntentSender target =
ActivityManagerNative.getDefault().getIntentSender(
ActivityManager.INTENT_SENDER_SERVICE, packageName,
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index 2260d7e..10e6fb2 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -522,7 +522,7 @@
IActivityManager am = ActivityManagerNative.getDefault();
IBinder binder = null;
try {
- service.prepareToLeaveProcess();
+ service.prepareToLeaveProcess(myContext);
binder = am.peekService(service, service.resolveTypeIfNeeded(
myContext.getContentResolver()), myContext.getOpPackageName());
} catch (RemoteException e) {
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index 6dfefac..0ec58ea 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -823,14 +823,14 @@
*
* @hide
*/
- public void prepareToLeaveProcess() {
+ public void prepareToLeaveProcess(boolean leavingPackage) {
final int size = mItems.size();
for (int i = 0; i < size; i++) {
final Item item = mItems.get(i);
if (item.mIntent != null) {
- item.mIntent.prepareToLeaveProcess();
+ item.mIntent.prepareToLeaveProcess(leavingPackage);
}
- if (item.mUri != null && StrictMode.vmFileUriExposureEnabled()) {
+ if (item.mUri != null && StrictMode.vmFileUriExposureEnabled() && leavingPackage) {
item.mUri.checkFileUriExposed("ClipData.Item.getUri()");
}
}
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 5653cad..e67da2b 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -118,7 +118,7 @@
public void setPrimaryClip(ClipData clip) {
try {
if (clip != null) {
- clip.prepareToLeaveProcess();
+ clip.prepareToLeaveProcess(true);
}
getService().setPrimaryClip(clip, mContext.getOpPackageName());
} catch (RemoteException e) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 49b8363..ee469da 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -8912,23 +8912,46 @@
*
* @hide
*/
- public void prepareToLeaveProcess() {
+ public void prepareToLeaveProcess(Context context) {
+ final boolean leavingPackage = (mComponent == null)
+ || !Objects.equals(mComponent.getPackageName(), context.getPackageName());
+ prepareToLeaveProcess(leavingPackage);
+ }
+
+ /**
+ * Prepare this {@link Intent} to leave an app process.
+ *
+ * @hide
+ */
+ public void prepareToLeaveProcess(boolean leavingPackage) {
setAllowFds(false);
if (mSelector != null) {
- mSelector.prepareToLeaveProcess();
+ mSelector.prepareToLeaveProcess(leavingPackage);
}
if (mClipData != null) {
- mClipData.prepareToLeaveProcess();
+ mClipData.prepareToLeaveProcess(leavingPackage);
}
- if (mData != null && StrictMode.vmFileUriExposureEnabled()) {
- // There are several ACTION_MEDIA_* broadcasts that send file://
- // Uris, so only check common actions.
- if (ACTION_VIEW.equals(mAction) ||
- ACTION_EDIT.equals(mAction) ||
- ACTION_ATTACH_DATA.equals(mAction)) {
- mData.checkFileUriExposed("Intent.getData()");
+ if (mData != null && StrictMode.vmFileUriExposureEnabled() && leavingPackage) {
+ switch (mAction) {
+ case ACTION_MEDIA_REMOVED:
+ case ACTION_MEDIA_UNMOUNTED:
+ case ACTION_MEDIA_CHECKING:
+ case ACTION_MEDIA_NOFS:
+ case ACTION_MEDIA_MOUNTED:
+ case ACTION_MEDIA_SHARED:
+ case ACTION_MEDIA_UNSHARED:
+ case ACTION_MEDIA_BAD_REMOVAL:
+ case ACTION_MEDIA_UNMOUNTABLE:
+ case ACTION_MEDIA_EJECT:
+ case ACTION_MEDIA_SCANNER_STARTED:
+ case ACTION_MEDIA_SCANNER_FINISHED:
+ case ACTION_MEDIA_SCANNER_SCAN_FILE:
+ // Ignore legacy actions
+ break;
+ default:
+ mData.checkFileUriExposed("Intent.getData()");
}
}
}
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 4a8dfbc..53b027b 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -2343,7 +2343,7 @@
*/
public void checkFileUriExposed(String location) {
if ("file".equals(getScheme())) {
- StrictMode.onFileUriExposed(location);
+ StrictMode.onFileUriExposed(this, location);
}
}
diff --git a/core/java/android/os/FileUriExposedException.java b/core/java/android/os/FileUriExposedException.java
new file mode 100644
index 0000000..e47abe2
--- /dev/null
+++ b/core/java/android/os/FileUriExposedException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 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;
+
+import android.content.Intent;
+
+/**
+ * The exception that is thrown when an application exposes a {@code file://}
+ * {@link android.net.Uri} to another app.
+ * <p>
+ * This exposure is discouraged since the receiving app may not have access to
+ * the shared path. For example, the receiving app may not have requested the
+ * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} runtime permission,
+ * or the platform may be sharing the {@link android.net.Uri} across user
+ * profile boundaries.
+ * <p>
+ * Instead, apps should use {@code content://} Uris so the platform can extend
+ * temporary permission for the receiving app to access the resource.
+ * <p>
+ * This is only thrown for applications targeting {@link Build.VERSION_CODES#N}
+ * or higher. Applications targeting earlier SDK versions are allowed to share
+ * {@code file://} {@link android.net.Uri}, but it's strongly discouraged.
+ *
+ * @see android.support.v4.content.FileProvider
+ * @see Intent#FLAG_GRANT_READ_URI_PERMISSION
+ */
+public class FileUriExposedException extends RuntimeException {
+ public FileUriExposedException(String message) {
+ super(message);
+ }
+}
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index f1672df..91d88da 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.net.Uri;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Printer;
@@ -46,7 +47,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -243,42 +243,15 @@
// Byte 3: Penalty
- /**
- * @hide
- */
+ /** {@hide} */
public static final int PENALTY_LOG = 0x01 << 16; // normal android.util.Log
-
- // Used for both process and thread policy:
-
- /**
- * @hide
- */
+ /** {@hide} */
public static final int PENALTY_DIALOG = 0x02 << 16;
-
- /**
- * Death on any detected violation.
- *
- * @hide
- */
+ /** {@hide} */
public static final int PENALTY_DEATH = 0x04 << 16;
-
- /**
- * Death just for detected network usage.
- *
- * @hide
- */
- public static final int PENALTY_DEATH_ON_NETWORK = 0x08 << 16;
-
- /**
- * Flash the screen during violations.
- *
- * @hide
- */
+ /** {@hide} */
public static final int PENALTY_FLASH = 0x10 << 16;
-
- /**
- * @hide
- */
+ /** {@hide} */
public static final int PENALTY_DROPBOX = 0x20 << 16;
/**
@@ -294,12 +267,28 @@
*/
public static final int PENALTY_GATHER = 0x40 << 16;
+ // Byte 4: Special cases
+
+ /**
+ * Death when network traffic is detected on main thread.
+ *
+ * @hide
+ */
+ public static final int PENALTY_DEATH_ON_NETWORK = 0x01 << 24;
+
/**
* Death when cleartext network traffic is detected.
*
* @hide
*/
- public static final int PENALTY_DEATH_ON_CLEARTEXT_NETWORK = 0x80 << 16;
+ public static final int PENALTY_DEATH_ON_CLEARTEXT_NETWORK = 0x02 << 24;
+
+ /**
+ * Death when file exposure is detected.
+ *
+ * @hide
+ */
+ public static final int PENALTY_DEATH_ON_FILE_URI_EXPOSURE = 0x04 << 24;
/**
* Mask of all the penalty bits valid for thread policies.
@@ -312,7 +301,7 @@
* Mask of all the penalty bits valid for VM policies.
*/
private static final int VM_PENALTY_MASK = PENALTY_LOG | PENALTY_DEATH | PENALTY_DROPBOX
- | PENALTY_DEATH_ON_CLEARTEXT_NETWORK;
+ | PENALTY_DEATH_ON_CLEARTEXT_NETWORK | PENALTY_DEATH_ON_FILE_URI_EXPOSURE;
/** {@hide} */
public static final int NETWORK_POLICY_ACCEPT = 0;
@@ -748,10 +737,22 @@
}
/**
- * Detect when a {@code file://} {@link android.net.Uri} is exposed beyond this
- * app. The receiving app may not have access to the sent path.
- * Instead, when sharing files between apps, {@code content://}
- * should be used with permission grants.
+ * Detect when this application exposes a {@code file://}
+ * {@link android.net.Uri} to another app.
+ * <p>
+ * This exposure is discouraged since the receiving app may not have
+ * access to the shared path. For example, the receiving app may not
+ * have requested the
+ * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} runtime
+ * permission, or the platform may be sharing the
+ * {@link android.net.Uri} across user profile boundaries.
+ * <p>
+ * Instead, apps should use {@code content://} Uris so the platform
+ * can extend temporary permission for the receiving app to access
+ * the resource.
+ *
+ * @see android.support.v4.content.FileProvider
+ * @see Intent#FLAG_GRANT_READ_URI_PERMISSION
*/
public Builder detectFileUriExposure() {
return enable(DETECT_VM_FILE_URI_EXPOSURE);
@@ -798,6 +799,16 @@
}
/**
+ * Crashes the whole process when a {@code file://}
+ * {@link android.net.Uri} is exposed beyond this app.
+ *
+ * @see #detectFileUriExposure()
+ */
+ public Builder penaltyDeathOnFileUriExposure() {
+ return enable(PENALTY_DEATH_ON_FILE_URI_EXPOSURE);
+ }
+
+ /**
* Log detected violations to the system log.
*/
public Builder penaltyLog() {
@@ -1111,6 +1122,25 @@
}
/**
+ * Used by the framework to make file usage a fatal error.
+ *
+ * @hide
+ */
+ public static void enableDeathOnFileUriExposure() {
+ sVmPolicyMask |= DETECT_VM_FILE_URI_EXPOSURE | PENALTY_DEATH_ON_FILE_URI_EXPOSURE;
+ }
+
+ /**
+ * Used by lame internal apps that haven't done the hard work to get
+ * themselves off file:// Uris yet.
+ *
+ * @hide
+ */
+ public static void disableDeathOnFileUriExposure() {
+ sVmPolicyMask &= ~(DETECT_VM_FILE_URI_EXPOSURE | PENALTY_DEATH_ON_FILE_URI_EXPOSURE);
+ }
+
+ /**
* Parses the BlockGuard policy mask out from the Exception's
* getMessage() String value. Kinda gross, but least
* invasive. :/
@@ -1755,9 +1785,13 @@
/**
* @hide
*/
- public static void onFileUriExposed(String location) {
- final String message = "file:// Uri exposed through " + location;
- onVmPolicyViolation(null, new Throwable(message));
+ public static void onFileUriExposed(Uri uri, String location) {
+ final String message = uri + " exposed beyond app through " + location;
+ if ((sVmPolicyMask & PENALTY_DEATH_ON_FILE_URI_EXPOSURE) != 0) {
+ throw new FileUriExposedException(message);
+ } else {
+ onVmPolicyViolation(null, new Throwable(message));
+ }
}
/**
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 0c6a0c6..6ff9fe7 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1170,7 +1170,7 @@
}
try {
intent.migrateExtraStreamToClipData();
- intent.prepareToLeaveProcess();
+ intent.prepareToLeaveProcess(mContext);
int res = mSystemService.startVoiceActivity(mToken, intent,
intent.resolveType(mContext.getContentResolver()));
Instrumentation.checkStartActivityResult(res, intent);