diff options
Diffstat (limited to 'core/java/android')
36 files changed, 578 insertions, 231 deletions
diff --git a/core/java/android/animation/RectEvaluator.java b/core/java/android/animation/RectEvaluator.java new file mode 100644 index 0000000..10932bb --- /dev/null +++ b/core/java/android/animation/RectEvaluator.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013 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.animation; + +import android.graphics.Rect; + +/** + * This evaluator can be used to perform type interpolation between <code>int</code> values. + */ +public class RectEvaluator implements TypeEvaluator<Rect> { + + /** + * This function returns the result of linearly interpolating the start and + * end Rect values, with <code>fraction</code> representing the proportion + * between the start and end values. The calculation is a simple parametric + * calculation on each of the separate components in the Rect objects + * (left, top, right, and bottom). + * + * @param fraction The fraction from the starting to the ending values + * @param startValue The start Rect + * @param endValue The end Rect + * @return A linear interpolation between the start and end values, given the + * <code>fraction</code> parameter. + */ + @Override + public Rect evaluate(float fraction, Rect startValue, Rect endValue) { + return new Rect(startValue.left + (int)((endValue.left - startValue.left) * fraction), + startValue.top + (int)((endValue.top - startValue.top) * fraction), + startValue.right + (int)((endValue.right - startValue.right) * fraction), + startValue.bottom + (int)((endValue.bottom - startValue.bottom) * fraction)); + } +} diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 87c2d8c..31074e2 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -3513,7 +3513,8 @@ public class Activity extends ContextThemeWrapper try { String resolvedType = null; if (fillInIntent != null) { - fillInIntent.setAllowFds(false); + fillInIntent.migrateExtraStreamToClipData(); + fillInIntent.prepareToLeaveProcess(); resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver()); } int result = ActivityManagerNative.getDefault() @@ -3738,7 +3739,8 @@ public class Activity extends ContextThemeWrapper if (mParent == null) { int result = ActivityManager.START_RETURN_INTENT_TO_CALLER; try { - intent.setAllowFds(false); + intent.migrateExtraStreamToClipData(); + intent.prepareToLeaveProcess(); result = ActivityManagerNative.getDefault() .startActivity(mMainThread.getApplicationThread(), getBasePackageName(), intent, intent.resolveTypeIfNeeded(getContentResolver()), @@ -3808,7 +3810,8 @@ public class Activity extends ContextThemeWrapper public boolean startNextMatchingActivity(Intent intent, Bundle options) { if (mParent == null) { try { - intent.setAllowFds(false); + intent.migrateExtraStreamToClipData(); + intent.prepareToLeaveProcess(); return ActivityManagerNative.getDefault() .startNextMatchingActivity(mToken, intent, options); } catch (RemoteException e) { @@ -4162,7 +4165,7 @@ public class Activity extends ContextThemeWrapper if (false) Log.v(TAG, "Finishing self: token=" + mToken); try { if (resultData != null) { - resultData.setAllowFds(false); + resultData.prepareToLeaveProcess(); } if (ActivityManagerNative.getDefault() .finishActivity(mToken, resultCode, resultData)) { @@ -4314,7 +4317,7 @@ public class Activity extends ContextThemeWrapper int flags) { String packageName = getPackageName(); try { - data.setAllowFds(false); + data.prepareToLeaveProcess(); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName, @@ -4993,9 +4996,10 @@ public class Activity extends ContextThemeWrapper resultData = mResultData; } if (resultData != null) { - resultData.setAllowFds(false); + resultData.prepareToLeaveProcess(); } try { + upIntent.prepareToLeaveProcess(); return ActivityManagerNative.getDefault().navigateUpTo(mToken, upIntent, resultCode, resultData); } catch (RemoteException e) { diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 459e49c..9bf8830 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1020,7 +1020,8 @@ class ContextImpl extends Context { try { String resolvedType = null; if (fillInIntent != null) { - fillInIntent.setAllowFds(false); + fillInIntent.migrateExtraStreamToClipData(); + fillInIntent.prepareToLeaveProcess(); resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver()); } int result = ActivityManagerNative.getDefault() @@ -1040,7 +1041,7 @@ class ContextImpl extends Context { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false, @@ -1054,7 +1055,7 @@ class ContextImpl extends Context { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, @@ -1068,7 +1069,7 @@ class ContextImpl extends Context { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermission, appOp, false, false, @@ -1083,7 +1084,7 @@ class ContextImpl extends Context { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, true, false, @@ -1126,7 +1127,7 @@ class ContextImpl extends Context { } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, receiverPermission, appOp, @@ -1139,7 +1140,7 @@ class ContextImpl extends Context { public void sendBroadcastAsUser(Intent intent, UserHandle user) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false, user.getIdentifier()); @@ -1152,7 +1153,7 @@ class ContextImpl extends Context { String receiverPermission) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, false, false, @@ -1184,7 +1185,7 @@ class ContextImpl extends Context { } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, receiverPermission, @@ -1198,7 +1199,7 @@ class ContextImpl extends Context { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, true, @@ -1232,7 +1233,7 @@ class ContextImpl extends Context { } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, null, @@ -1249,7 +1250,7 @@ class ContextImpl extends Context { intent.setDataAndType(intent.getData(), resolvedType); } try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().unbroadcastIntent( mMainThread.getApplicationThread(), intent, getUserId()); } catch (RemoteException e) { @@ -1260,7 +1261,7 @@ class ContextImpl extends Context { public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, true, user.getIdentifier()); @@ -1292,7 +1293,7 @@ class ContextImpl extends Context { } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, null, @@ -1309,7 +1310,7 @@ class ContextImpl extends Context { intent.setDataAndType(intent.getData(), resolvedType); } try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().unbroadcastIntent( mMainThread.getApplicationThread(), intent, user.getIdentifier()); } catch (RemoteException e) { @@ -1393,7 +1394,7 @@ class ContextImpl extends Context { @Override public ComponentName startServiceAsUser(Intent service, UserHandle user) { try { - service.setAllowFds(false); + service.prepareToLeaveProcess(); ComponentName cn = ActivityManagerNative.getDefault().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier()); @@ -1417,7 +1418,7 @@ class ContextImpl extends Context { @Override public boolean stopServiceAsUser(Intent service, UserHandle user) { try { - service.setAllowFds(false); + service.prepareToLeaveProcess(); int res = ActivityManagerNative.getDefault().stopService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier()); @@ -1459,7 +1460,7 @@ class ContextImpl extends Context { < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { flags |= BIND_WAIVE_PRIORITY; } - service.setAllowFds(false); + service.prepareToLeaveProcess(); 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 e7bf305..e0dfb25 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1410,8 +1410,8 @@ public class Instrumentation { } } try { - intent.setAllowFds(false); intent.migrateExtraStreamToClipData(); + intent.prepareToLeaveProcess(); int result = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), @@ -1467,7 +1467,8 @@ public class Instrumentation { try { String[] resolvedTypes = new String[intents.length]; for (int i=0; i<intents.length; i++) { - intents[i].setAllowFds(false); + intents[i].migrateExtraStreamToClipData(); + intents[i].prepareToLeaveProcess(); resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver()); } int result = ActivityManagerNative.getDefault() @@ -1526,8 +1527,8 @@ public class Instrumentation { } } try { - intent.setAllowFds(false); intent.migrateExtraStreamToClipData(); + intent.prepareToLeaveProcess(); int result = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), @@ -1586,8 +1587,8 @@ public class Instrumentation { } } try { - intent.setAllowFds(false); intent.migrateExtraStreamToClipData(); + intent.prepareToLeaveProcess(); int result = ActivityManagerNative.getDefault() .startActivityAsUser(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 5e69128..dbafc78 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -21,6 +21,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.StrictMode; import android.os.UserHandle; import android.util.Log; @@ -126,6 +127,9 @@ public class NotificationManager String pkg = mContext.getPackageName(); if (notification.sound != null) { notification.sound = notification.sound.getCanonicalUri(); + if (StrictMode.vmFileUriExposureEnabled()) { + notification.sound.checkFileUriExposed("Notification.sound"); + } } if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); try { @@ -148,6 +152,9 @@ public class NotificationManager String pkg = mContext.getPackageName(); if (notification.sound != null) { notification.sound = notification.sound.getCanonicalUri(); + if (StrictMode.vmFileUriExposureEnabled()) { + notification.sound.checkFileUriExposed("Notification.sound"); + } } if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); try { diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 20114cc..25c790f 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -260,8 +260,8 @@ public final class PendingIntent implements Parcelable { String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; try { - intent.setAllowFds(false); intent.migrateExtraStreamToClipData(); + intent.prepareToLeaveProcess(); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( ActivityManager.INTENT_SENDER_ACTIVITY, packageName, @@ -285,8 +285,8 @@ public final class PendingIntent implements Parcelable { String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; try { - intent.setAllowFds(false); intent.migrateExtraStreamToClipData(); + intent.prepareToLeaveProcess(); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( ActivityManager.INTENT_SENDER_ACTIVITY, packageName, @@ -401,7 +401,8 @@ public final class PendingIntent implements Parcelable { String packageName = context.getPackageName(); String[] resolvedTypes = new String[intents.length]; for (int i=0; i<intents.length; i++) { - intents[i].setAllowFds(false); + intents[i].migrateExtraStreamToClipData(); + intents[i].prepareToLeaveProcess(); resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver()); } try { @@ -426,7 +427,8 @@ public final class PendingIntent implements Parcelable { String packageName = context.getPackageName(); String[] resolvedTypes = new String[intents.length]; for (int i=0; i<intents.length; i++) { - intents[i].setAllowFds(false); + intents[i].migrateExtraStreamToClipData(); + intents[i].prepareToLeaveProcess(); resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver()); } try { @@ -482,7 +484,7 @@ public final class PendingIntent implements Parcelable { String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( ActivityManager.INTENT_SENDER_BROADCAST, packageName, @@ -526,7 +528,7 @@ public final class PendingIntent implements Parcelable { String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( ActivityManager.INTENT_SENDER_SERVICE, packageName, diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 83e95ca..3c1ec90 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -262,6 +262,26 @@ public final class BluetoothDevice implements Parcelable { public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY"; /** + * Bluetooth device type, Unknown + */ + public static final int DEVICE_TYPE_UNKNOWN = 0; + + /** + * Bluetooth device type, Classic - BR/EDR devices + */ + public static final int DEVICE_TYPE_CLASSIC = 1; + + /** + * Bluetooth device type, Low Energy - LE-only + */ + public static final int DEVICE_TYPE_LE = 2; + + /** + * Bluetooth device type, Dual Mode - BR/EDR/LE + */ + public static final int DEVICE_TYPE_DUAL = 3; + + /** * Broadcast Action: This intent is used to broadcast the {@link UUID} * wrapped as a {@link android.os.ParcelUuid} of the remote device after it * has been fetched. This intent is sent only when the UUIDs of the remote @@ -602,6 +622,26 @@ public final class BluetoothDevice implements Parcelable { } /** + * Get the Bluetooth device type of the remote device. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @return the device type {@link #DEVICE_TYPE_CLASSIC}, {@link #DEVICE_TYPE_LE} + * {@link #DEVICE_TYPE_DUAL}. + * {@link #DEVICE_TYPE_UNKNOWN} if it's not available + */ + public int getType() { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot get Remote Device type"); + return DEVICE_TYPE_UNKNOWN; + } + try { + return sService.getRemoteType(this); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return DEVICE_TYPE_UNKNOWN; + } + + /** * Get the Bluetooth alias of the remote device. * <p>Alias is the locally modified name of a remote device. * @@ -1139,8 +1179,8 @@ public final class BluetoothDevice implements Parcelable { * device becomes available (true). * @throws IllegalArgumentException if callback is null */ - public BluetoothGatt connectGattServer(Context context, boolean autoConnect, - BluetoothGattCallback callback) { + public BluetoothGatt connectGatt(Context context, boolean autoConnect, + BluetoothGattCallback callback) { // TODO(Bluetooth) check whether platform support BLE // Do the check here or in GattServer? BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java index f9ce6ea..bffe64b 100644 --- a/core/java/android/bluetooth/BluetoothGatt.java +++ b/core/java/android/bluetooth/BluetoothGatt.java @@ -43,7 +43,7 @@ import java.util.UUID; * with Bluetooth Smart or Smart Ready devices. * * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback} - * and call {@link BluetoothDevice#connectGattServer} to get a instance of this class. + * and call {@link BluetoothDevice#connectGatt} to get a instance of this class. * GATT capable devices can be discovered using the Bluetooth device discovery or BLE * scan process. */ @@ -66,6 +66,7 @@ public final class BluetoothGatt implements BluetoothProfile { private static final int CONN_STATE_CONNECTING = 1; private static final int CONN_STATE_CONNECTED = 2; private static final int CONN_STATE_DISCONNECTING = 3; + private static final int CONN_STATE_CLOSED = 4; private List<BluetoothGattService> mServices; @@ -135,7 +136,7 @@ public final class BluetoothGatt implements BluetoothProfile { } mClientIf = clientIf; if (status != GATT_SUCCESS) { - mCallback.onConnectionStateChange(mDevice, GATT_FAILURE, + mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE, BluetoothProfile.STATE_DISCONNECTED); synchronized(mStateLock) { mConnState = CONN_STATE_IDLE; @@ -164,7 +165,7 @@ public final class BluetoothGatt implements BluetoothProfile { int profileState = connected ? BluetoothProfile.STATE_CONNECTED : BluetoothProfile.STATE_DISCONNECTED; try { - mCallback.onConnectionStateChange(mDevice, status, profileState); + mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -291,7 +292,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } try { - mCallback.onServicesDiscovered(mDevice, status); + mCallback.onServicesDiscovered(BluetoothGatt.this, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -338,7 +339,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (status == 0) characteristic.setValue(value); try { - mCallback.onCharacteristicRead(characteristic, status); + mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -384,7 +385,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetry = false; try { - mCallback.onCharacteristicWrite(characteristic, status); + mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -415,7 +416,7 @@ public final class BluetoothGatt implements BluetoothProfile { characteristic.setValue(value); try { - mCallback.onCharacteristicChanged(characteristic); + mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -464,7 +465,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetry = true; try { - mCallback.onDescriptorRead(descriptor, status); + mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -512,7 +513,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetry = false; try { - mCallback.onDescriptorWrite(descriptor, status); + mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -529,7 +530,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } try { - mCallback.onReliableWriteCompleted(mDevice, status); + mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -546,7 +547,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } try { - mCallback.onReadRemoteRssi(mDevice, rssi, status); + mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -563,12 +564,13 @@ public final class BluetoothGatt implements BluetoothProfile { } /** - * Close the connection to the gatt service. + * Close this Bluetooth GATT client. */ - /*package*/ void close() { + public void close() { if (DBG) Log.d(TAG, "close()"); unregisterApp(); + mConnState = CONN_STATE_CLOSED; } /** @@ -694,7 +696,35 @@ public final class BluetoothGatt implements BluetoothProfile { } catch (RemoteException e) { Log.e(TAG,"",e); } - // TBD deregister after conneciton is torn down + } + + /** + * Connect back to remote device. + * + * <p>This method is used to re-connect to a remote device after the + * connection has been dropped. If the device is not in range, the + * re-connection will be triggered once the device is back in range. + * + * @return true, if the connection attempt was initiated successfully + */ + public boolean connect() { + try { + mService.clientConnect(mClientIf, mDevice.getAddress(), + false); // autoConnect is inverse of "isDirect" + return true; + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + } + + /** + * Return the remote bluetooth device this GATT client targets to + * + * @return remote bluetooth device + */ + public BluetoothDevice getDevice() { + return mDevice; } /** diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java index c9e5fea..2259c1e 100644 --- a/core/java/android/bluetooth/BluetoothGattCallback.java +++ b/core/java/android/bluetooth/BluetoothGattCallback.java @@ -16,23 +16,22 @@ package android.bluetooth; -import android.bluetooth.BluetoothDevice; - /** * This abstract class is used to implement {@link BluetoothGatt} callbacks. */ public abstract class BluetoothGattCallback { /** - * Callback indicating when a remote device has been connected or disconnected. + * Callback indicating when GATT client has connected/disconnected to/from a remote + * GATT server. * - * @param device Remote device that has been connected or disconnected. + * @param gatt GATT client * @param status Status of the connect or disconnect operation. * @param newState Returns the new connection state. Can be one of * {@link BluetoothProfile#STATE_DISCONNECTED} or * {@link BluetoothProfile#STATE_CONNECTED} */ - public void onConnectionStateChange(BluetoothDevice device, int status, + public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { } @@ -40,22 +39,23 @@ public abstract class BluetoothGattCallback { * Callback invoked when the list of remote services, characteristics and descriptors * for the remote device have been updated, ie new services have been discovered. * - * @param device Remote device + * @param gatt GATT client invoked {@link BluetoothGatt#discoverServices} * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device * has been explored successfully. */ - public void onServicesDiscovered(BluetoothDevice device, int status) { + public void onServicesDiscovered(BluetoothGatt gatt, int status) { } /** * Callback reporting the result of a characteristic read operation. * + * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic} * @param characteristic Characteristic that was read from the associated * remote device. * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation * was completed successfully. */ - public void onCharacteristicRead(BluetoothGattCharacteristic characteristic, + public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { } @@ -68,52 +68,59 @@ public abstract class BluetoothGattCallback { * value to the desired value to be written. If the values don't match, * the application must abort the reliable write transaction. * + * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic} * @param characteristic Characteristic that was written to the associated * remote device. * @param status The result of the write operation */ - public void onCharacteristicWrite(BluetoothGattCharacteristic characteristic, - int status) { + public void onCharacteristicWrite(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { } /** * Callback triggered as a result of a remote characteristic notification. * + * @param gatt GATT client the characteristic is associated with * @param characteristic Characteristic that has been updated as a result * of a remote notification event. */ - public void onCharacteristicChanged(BluetoothGattCharacteristic characteristic) { + public void onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { } /** * Callback reporting the result of a descriptor read operation. * + * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor} * @param descriptor Descriptor that was read from the associated * remote device. * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation * was completed successfully */ - public void onDescriptorRead(BluetoothGattDescriptor descriptor, int status) { + public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, + int status) { } /** * Callback indicating the result of a descriptor write operation. * + * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor} * @param descriptor Descriptor that was writte to the associated * remote device. * @param status The result of the write operation */ - public void onDescriptorWrite(BluetoothGattDescriptor descriptor, int status) { + public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, + int status) { } /** * Callback invoked when a reliable write transaction has been completed. * - * @param device Remote device + * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite} * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write * transaction was executed successfully */ - public void onReliableWriteCompleted(BluetoothDevice device, int status) { + public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { } /** @@ -122,10 +129,10 @@ public abstract class BluetoothGattCallback { * This callback is triggered in response to the * {@link BluetoothGatt#readRemoteRssi} function. * - * @param device Identifies the remote device + * @param gatt GATT client invoked {@link BluetoothGatt#readRemoteRssi} * @param rssi The RSSI value for the remote device * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully */ - public void onReadRemoteRssi(BluetoothDevice device, int rssi, int status) { + public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { } } diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java index d63d97e..033f079 100644 --- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -22,6 +22,10 @@ import java.util.UUID; /** * Represents a Bluetooth GATT Characteristic + * + * <p>A GATT characteristic is a basic data element used to construct a GATT service, + * {@link BluetoothGattService}. The characteristic contains a value as well as + * additional information and optional GATT descriptors, {@link BluetoothGattDescriptor}. */ public class BluetoothGattCharacteristic { diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java index 6ba2db7..1cd6878 100644 --- a/core/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java @@ -20,6 +20,10 @@ import java.util.UUID; /** * Represents a Bluetooth GATT Descriptor + * + * <p> GATT Descriptors contain additional information and attributes of a GATT + * characteristic, {@link BluetoothGattCharacteristic}. They can be used to describe + * the characteristic's features or to control certain behaviours of the characteristic. */ public class BluetoothGattDescriptor { diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java index d1f4b82..644c619 100644 --- a/core/java/android/bluetooth/BluetoothGattServer.java +++ b/core/java/android/bluetooth/BluetoothGattServer.java @@ -554,9 +554,10 @@ public final class BluetoothGattServer implements BluetoothProfile { List<BluetoothGattDescriptor> descriptors = characteristic.getDescriptors(); for (BluetoothGattDescriptor descriptor: descriptors) { + permission = ((characteristic.getKeySize() - 7) << 12) + + descriptor.getPermissions(); mService.addDescriptor(mServerIf, - new ParcelUuid(descriptor.getUuid()), - descriptor.getPermissions()); + new ParcelUuid(descriptor.getUuid()), permission); } } diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java index c3b3cfe..39a435b 100644 --- a/core/java/android/bluetooth/BluetoothGattService.java +++ b/core/java/android/bluetooth/BluetoothGattService.java @@ -23,6 +23,9 @@ import java.util.UUID; /** * Represents a Bluetooth GATT Service + * + * <p> Gatt Service contains a collection of {@link BluetoothGattCharacteristic}, + * as well as referenced services. */ public class BluetoothGattService { diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index d016c26..80806f9 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -60,6 +60,7 @@ interface IBluetooth int getBondState(in BluetoothDevice device); String getRemoteName(in BluetoothDevice device); + int getRemoteType(in BluetoothDevice device); String getRemoteAlias(in BluetoothDevice device); boolean setRemoteAlias(in BluetoothDevice device, in String name); int getRemoteClass(in BluetoothDevice device); diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java index 4f42d50..9a32fdf 100644 --- a/core/java/android/content/BroadcastReceiver.java +++ b/core/java/android/content/BroadcastReceiver.java @@ -520,7 +520,7 @@ public abstract class BroadcastReceiver { IActivityManager am = ActivityManagerNative.getDefault(); IBinder binder = null; try { - service.setAllowFds(false); + service.prepareToLeaveProcess(); binder = am.peekService(service, service.resolveTypeIfNeeded( myContext.getContentResolver())); } catch (RemoteException e) { diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java index 88f1a3d..50c4fed 100644 --- a/core/java/android/content/ClipData.java +++ b/core/java/android/content/ClipData.java @@ -21,6 +21,7 @@ import android.graphics.Bitmap; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.os.StrictMode; import android.text.Html; import android.text.Spannable; import android.text.SpannableStringBuilder; @@ -790,6 +791,24 @@ public class ClipData implements Parcelable { return mItems.get(index); } + /** + * Prepare this {@link ClipData} to leave an app process. + * + * @hide + */ + public void prepareToLeaveProcess() { + 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(); + } + if (item.mUri != null && StrictMode.vmFileUriExposureEnabled()) { + item.mUri.checkFileUriExposed("ClipData.Item.getUri()"); + } + } + } + @Override public String toString() { StringBuilder b = new StringBuilder(128); diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java index 88a4229..69f9d4a 100644 --- a/core/java/android/content/ClipboardManager.java +++ b/core/java/android/content/ClipboardManager.java @@ -22,6 +22,7 @@ import android.os.RemoteException; import android.os.Handler; import android.os.IBinder; import android.os.ServiceManager; +import android.os.StrictMode; import android.util.Log; import java.util.ArrayList; @@ -118,6 +119,9 @@ public class ClipboardManager extends android.text.ClipboardManager { */ public void setPrimaryClip(ClipData clip) { try { + if (clip != null) { + clip.prepareToLeaveProcess(); + } getService().setPrimaryClip(clip, mContext.getBasePackageName()); } catch (RemoteException e) { } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 0efd6b0..97ad7dd 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -32,6 +32,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; +import android.os.StrictMode; import android.util.AttributeSet; import android.util.Log; @@ -2578,6 +2579,14 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_SHOW_BRIGHTNESS_DIALOG = "android.intent.action.SHOW_BRIGHTNESS_DIALOG"; + /** + * Broadcast Action: A global button was pressed. Includes a single + * extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that + * caused the broadcast. + * @hide + */ + public static final String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent categories (see addCategory()). @@ -6958,6 +6967,32 @@ public class Intent implements Parcelable, Cloneable { } /** + * Prepare this {@link Intent} to leave an app process. + * + * @hide + */ + public void prepareToLeaveProcess() { + setAllowFds(false); + + if (mSelector != null) { + mSelector.prepareToLeaveProcess(); + } + if (mClipData != null) { + mClipData.prepareToLeaveProcess(); + } + + 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()"); + } + } + } + + /** * Migrate any {@link #EXTRA_STREAM} in {@link #ACTION_SEND} and * {@link #ACTION_SEND_MULTIPLE} to {@link ClipData}. Also inspects nested * intents in {@link #ACTION_CHOOSER}. diff --git a/core/java/android/content/pm/ManifestDigest.java b/core/java/android/content/pm/ManifestDigest.java index 75505bc..409b5ae 100644 --- a/core/java/android/content/pm/ManifestDigest.java +++ b/core/java/android/content/pm/ManifestDigest.java @@ -18,10 +18,17 @@ package android.content.pm; import android.os.Parcel; import android.os.Parcelable; -import android.util.Base64; - +import android.util.Slog; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.Arrays; -import java.util.jar.Attributes; + +import libcore.io.IoUtils; /** * Represents the manifest digest for a package. This is suitable for comparison @@ -30,17 +37,17 @@ import java.util.jar.Attributes; * @hide */ public class ManifestDigest implements Parcelable { + private static final String TAG = "ManifestDigest"; + /** The digest of the manifest in our preferred order. */ private final byte[] mDigest; - /** Digest field names to look for in preferred order. */ - private static final String[] DIGEST_TYPES = { - "SHA1-Digest", "SHA-Digest", "MD5-Digest", - }; - /** What we print out first when toString() is called. */ private static final String TO_STRING_PREFIX = "ManifestDigest {mDigest="; + /** Digest algorithm to use. */ + private static final String DIGEST_ALGORITHM = "SHA-256"; + ManifestDigest(byte[] digest) { mDigest = digest; } @@ -49,26 +56,32 @@ public class ManifestDigest implements Parcelable { mDigest = source.createByteArray(); } - static ManifestDigest fromAttributes(Attributes attributes) { - if (attributes == null) { + static ManifestDigest fromInputStream(InputStream fileIs) { + if (fileIs == null) { return null; } - String encodedDigest = null; - - for (int i = 0; i < DIGEST_TYPES.length; i++) { - final String value = attributes.getValue(DIGEST_TYPES[i]); - if (value != null) { - encodedDigest = value; - break; - } + final MessageDigest md; + try { + md = MessageDigest.getInstance(DIGEST_ALGORITHM); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(DIGEST_ALGORITHM + " must be available", e); } - if (encodedDigest == null) { + final DigestInputStream dis = new DigestInputStream(new BufferedInputStream(fileIs), md); + try { + byte[] readBuffer = new byte[8192]; + while (dis.read(readBuffer, 0, readBuffer.length) != -1) { + // not using + } + } catch (IOException e) { + Slog.w(TAG, "Could not read manifest"); return null; + } finally { + IoUtils.closeQuietly(dis); } - final byte[] digest = Base64.decode(encodedDigest, Base64.DEFAULT); + final byte[] digest = md.digest(); return new ManifestDigest(digest); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 11f9be9..49cea3a 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -24,7 +24,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; -import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.PatternMatcher; @@ -54,10 +53,9 @@ import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import java.util.jar.Manifest; +import java.util.zip.ZipEntry; import com.android.internal.util.XmlUtils; @@ -567,6 +565,28 @@ public class PackageParser { return pkg; } + /** + * Gathers the {@link ManifestDigest} for {@code pkg} if it exists in the + * APK. If it successfully scanned the package and found the + * {@code AndroidManifest.xml}, {@code true} is returned. + */ + public boolean collectManifestDigest(Package pkg) { + try { + final JarFile jarFile = new JarFile(mArchiveSourcePath); + try { + final ZipEntry je = jarFile.getEntry(ANDROID_MANIFEST_FILENAME); + if (je != null) { + pkg.manifestDigest = ManifestDigest.fromInputStream(jarFile.getInputStream(je)); + } + } finally { + jarFile.close(); + } + return true; + } catch (IOException e) { + return false; + } + } + public boolean collectCertificates(Package pkg, int flags) { pkg.mSignatures = null; @@ -618,7 +638,6 @@ public class PackageParser { } } else { Enumeration<JarEntry> entries = jarFile.entries(); - final Manifest manifest = jarFile.getManifest(); while (entries.hasMoreElements()) { final JarEntry je = entries.nextElement(); if (je.isDirectory()) continue; @@ -629,8 +648,8 @@ public class PackageParser { continue; if (ANDROID_MANIFEST_FILENAME.equals(name)) { - final Attributes attributes = manifest.getAttributes(name); - pkg.manifestDigest = ManifestDigest.fromAttributes(attributes); + pkg.manifestDigest = + ManifestDigest.fromInputStream(jarFile.getInputStream(je)); } final Certificate[] localCerts = loadCertificates(jarFile, je, readBuffer); @@ -1379,6 +1398,17 @@ public class PackageParser { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES; } + /* + * b/8528162: Ignore the <uses-permission android:required> attribute if + * targetSdkVersion < JELLY_BEAN_MR2. There are lots of apps in the wild + * which are improperly using this attribute, even though it never worked. + */ + if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) { + for (int i = 0; i < pkg.requestedPermissionsRequired.size(); i++) { + pkg.requestedPermissionsRequired.set(i, Boolean.TRUE); + } + } + return pkg; } diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 52b238f..75f8b59 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -378,7 +378,7 @@ public class LinkProperties implements Parcelable { * @return {@code true} if both are identical, {@code false} otherwise. */ public boolean isIdenticalStackedLinks(LinkProperties target) { - if (!mStackedLinks.keys().equals(target.mStackedLinks.keys())) { + if (!mStackedLinks.keySet().equals(target.mStackedLinks.keySet())) { return false; } for (LinkProperties stacked : mStackedLinks.values()) { diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index cc6903d..4b022d9 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -20,6 +20,7 @@ import android.os.Environment; import android.os.Parcel; import android.os.Parcelable; import android.os.Environment.UserEnvironment; +import android.os.StrictMode; import android.util.Log; import java.io.File; import java.io.IOException; @@ -2326,4 +2327,16 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { return this; } } + + /** + * If this is a {@code file://} Uri, it will be reported to + * {@link StrictMode}. + * + * @hide + */ + public void checkFileUriExposed(String location) { + if ("file".equals(getScheme())) { + StrictMode.onFileUriExposed(location); + } + } } diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index f682abe..3267939 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -203,10 +203,15 @@ public final class StrictMode { */ public static final int DETECT_VM_REGISTRATION_LEAKS = 0x2000; // for VmPolicy + /** + * @hide + */ + private static final int DETECT_VM_FILE_URI_EXPOSURE = 0x4000; // for VmPolicy + private static final int ALL_VM_DETECT_BITS = DETECT_VM_CURSOR_LEAKS | DETECT_VM_CLOSABLE_LEAKS | DETECT_VM_ACTIVITY_LEAKS | DETECT_VM_INSTANCE_LEAKS | - DETECT_VM_REGISTRATION_LEAKS; + DETECT_VM_REGISTRATION_LEAKS | DETECT_VM_FILE_URI_EXPOSURE; /** * @hide @@ -628,7 +633,8 @@ public final class StrictMode { */ public Builder detectAll() { return enable(DETECT_VM_ACTIVITY_LEAKS | DETECT_VM_CURSOR_LEAKS - | DETECT_VM_CLOSABLE_LEAKS | DETECT_VM_REGISTRATION_LEAKS); + | DETECT_VM_CLOSABLE_LEAKS | DETECT_VM_REGISTRATION_LEAKS + | DETECT_VM_FILE_URI_EXPOSURE); } /** @@ -666,6 +672,16 @@ public final class StrictMode { } /** + * 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. + */ + public Builder detectFileUriExposure() { + return enable(DETECT_VM_FILE_URI_EXPOSURE); + } + + /** * Crashes the whole process on violation. This penalty runs at * the end of all enabled penalties so yo you'll still get * your logging or other violations before the process dies. @@ -1524,6 +1540,13 @@ public final class StrictMode { /** * @hide */ + public static boolean vmFileUriExposureEnabled() { + return (sVmPolicyMask & DETECT_VM_FILE_URI_EXPOSURE) != 0; + } + + /** + * @hide + */ public static void onSqliteObjectLeaked(String message, Throwable originStack) { onVmPolicyViolation(message, originStack); } @@ -1549,6 +1572,14 @@ public final class StrictMode { onVmPolicyViolation(null, originStack); } + /** + * @hide + */ + public static void onFileUriExposed(String location) { + final String message = "file:// Uri exposed through " + location; + onVmPolicyViolation(message, new Throwable(message)); + } + // Map from VM violation fingerprint to uptime millis. private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<Integer, Long>(); diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java index 2dd27f8..25af209 100644 --- a/core/java/android/provider/CalendarContract.java +++ b/core/java/android/provider/CalendarContract.java @@ -106,16 +106,13 @@ public final class CalendarContract { * {@link Activity#RESULT_OK} or {@link Activity#RESULT_CANCELED} to * acknowledge whether the action was handled or not. * - * The custom app should have an intent-filter like the following + * The custom app should have an intent filter like the following: * <pre> - * {@code - * <intent-filter> - * <action android:name="android.provider.calendar.action.HANDLE_CUSTOM_EVENT" /> - * <category android:name="android.intent.category.DEFAULT" /> - * <data android:mimeType="vnd.android.cursor.item/event" /> - * </intent-filter> - * } - * </pre> + * <intent-filter> + * <action android:name="android.provider.calendar.action.HANDLE_CUSTOM_EVENT" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <data android:mimeType="vnd.android.cursor.item/event" /> + * </intent-filter></pre> * <p> * Input: {@link Intent#getData} has the event URI. The extra * {@link #EXTRA_EVENT_BEGIN_TIME} has the start time of the instance. The @@ -123,7 +120,7 @@ public final class CalendarContract { * {@link EventsColumns#CUSTOM_APP_URI}. * <p> * Output: {@link Activity#RESULT_OK} if this was handled; otherwise - * {@link Activity#RESULT_CANCELED} + * {@link Activity#RESULT_CANCELED}. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_HANDLE_CUSTOM_EVENT = diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java index c365643..e1cc90e 100644 --- a/core/java/android/security/IKeystoreService.java +++ b/core/java/android/security/IKeystoreService.java @@ -444,6 +444,24 @@ public interface IKeystoreService extends IInterface { } return _result; } + + @Override + public int clear_uid(long uid) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + int _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeLong(uid); + mRemote.transact(Stub.TRANSACTION_clear_uid, _data, _reply, 0); + _reply.readException(); + _result = _reply.readInt(); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } } private static final String DESCRIPTOR = "android.security.keystore"; @@ -470,6 +488,7 @@ public interface IKeystoreService extends IInterface { static final int TRANSACTION_getmtime = IBinder.FIRST_CALL_TRANSACTION + 19; static final int TRANSACTION_duplicate = IBinder.FIRST_CALL_TRANSACTION + 20; static final int TRANSACTION_is_hardware_backed = IBinder.FIRST_CALL_TRANSACTION + 21; + static final int TRANSACTION_clear_uid = IBinder.FIRST_CALL_TRANSACTION + 22; /** * Cast an IBinder object into an IKeystoreService interface, generating @@ -559,4 +578,6 @@ public interface IKeystoreService extends IInterface { throws RemoteException; public int is_hardware_backed() throws RemoteException; + + public int clear_uid(long uid) throws RemoteException; } diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java index 9860588..2bc1c6a 100644 --- a/core/java/android/text/util/Linkify.java +++ b/core/java/android/text/util/Linkify.java @@ -16,6 +16,7 @@ package android.text.util; +import android.telephony.PhoneNumberUtils; import android.text.method.LinkMovementMethod; import android.text.method.MovementMethod; import android.text.style.URLSpan; @@ -32,9 +33,14 @@ import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.android.i18n.phonenumbers.PhoneNumberMatch; +import com.android.i18n.phonenumbers.PhoneNumberUtil; +import com.android.i18n.phonenumbers.PhoneNumberUtil.Leniency; + /** * Linkify take a piece of text and a regular expression and turns all of the * regex matches in the text into clickable links. This is particularly @@ -221,9 +227,7 @@ public class Linkify { } if ((mask & PHONE_NUMBERS) != 0) { - gatherLinks(links, text, Patterns.PHONE, - new String[] { "tel:" }, - sPhoneNumberMatchFilter, sPhoneNumberTransformFilter); + gatherTelLinks(links, text); } if ((mask & MAP_ADDRESSES) != 0) { @@ -443,6 +447,19 @@ public class Linkify { } } + private static final void gatherTelLinks(ArrayList<LinkSpec> links, Spannable s) { + PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); + Iterable<PhoneNumberMatch> matches = phoneUtil.findNumbers(s.toString(), + Locale.getDefault().getCountry(), Leniency.POSSIBLE, Long.MAX_VALUE); + for (PhoneNumberMatch match : matches) { + LinkSpec spec = new LinkSpec(); + spec.url = "tel:" + PhoneNumberUtils.normalizeNumber(match.rawString()); + spec.start = match.start(); + spec.end = match.end(); + links.add(spec); + } + } + private static final void gatherMapLinks(ArrayList<LinkSpec> links, Spannable s) { String string = s.toString(); String address; diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 7918823..8055077 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -451,10 +451,8 @@ public abstract class HardwareRenderer { * @param attachInfo AttachInfo tied to the specified view. * @param callbacks Callbacks invoked when drawing happens. * @param dirty The dirty rectangle to update, can be null. - * - * @return true if the dirty rect was ignored, false otherwise */ - abstract boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, + abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty); /** @@ -992,11 +990,7 @@ public abstract class HardwareRenderer { mCanvas = createCanvas(); mCanvas.setName(mName); } - if (mCanvas != null) { - setEnabled(true); - } else { - Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created"); - } + setEnabled(true); } return mCanvas != null; @@ -1340,7 +1334,7 @@ public abstract class HardwareRenderer { } @Override - boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, + void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) { if (canDraw()) { if (!hasDirtyRegions()) { @@ -1401,11 +1395,8 @@ public abstract class HardwareRenderer { } attachInfo.mIgnoreDirtyState = false; - return dirty == null; } } - - return false; } private DisplayList buildDisplayList(View view, HardwareCanvas canvas) { diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index a85a558..8ed4a86 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -175,6 +175,12 @@ interface IWindowManager int watchRotation(IRotationWatcher watcher); /** + * Remove a rotation watcher set using watchRotation. + * @hide + */ + void removeRotationWatcher(IRotationWatcher watcher); + + /** * Determine the preferred edge of the screen to pin the compact options menu against. * @return a Gravity value for the options menu panel * @hide diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java index 117c101..f5ee7ed 100644 --- a/core/java/android/view/InputEventReceiver.java +++ b/core/java/android/view/InputEventReceiver.java @@ -23,6 +23,8 @@ import android.os.MessageQueue; import android.util.Log; import android.util.SparseIntArray; +import java.lang.ref.WeakReference; + /** * Provides a low-level mechanism for an application to receive input events. * @hide @@ -42,7 +44,7 @@ public abstract class InputEventReceiver { // Map from InputEvent sequence numbers to dispatcher sequence numbers. private final SparseIntArray mSeqMap = new SparseIntArray(); - private static native int nativeInit(InputEventReceiver receiver, + private static native int nativeInit(WeakReference<InputEventReceiver> receiver, InputChannel inputChannel, MessageQueue messageQueue); private static native void nativeDispose(int receiverPtr); private static native void nativeFinishInputEvent(int receiverPtr, int seq, boolean handled); @@ -65,7 +67,8 @@ public abstract class InputEventReceiver { mInputChannel = inputChannel; mMessageQueue = looper.getQueue(); - mReceiverPtr = nativeInit(this, inputChannel, mMessageQueue); + mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this), + inputChannel, mMessageQueue); mCloseGuard.open("dispose"); } diff --git a/core/java/android/view/InputEventSender.java b/core/java/android/view/InputEventSender.java index adf63fe..be6a623 100644 --- a/core/java/android/view/InputEventSender.java +++ b/core/java/android/view/InputEventSender.java @@ -22,6 +22,8 @@ import android.os.Looper; import android.os.MessageQueue; import android.util.Log; +import java.lang.ref.WeakReference; + /** * Provides a low-level mechanism for an application to send input events. * @hide @@ -38,7 +40,7 @@ public abstract class InputEventSender { private InputChannel mInputChannel; private MessageQueue mMessageQueue; - private static native int nativeInit(InputEventSender sender, + private static native int nativeInit(WeakReference<InputEventSender> sender, InputChannel inputChannel, MessageQueue messageQueue); private static native void nativeDispose(int senderPtr); private static native boolean nativeSendKeyEvent(int senderPtr, int seq, KeyEvent event); @@ -60,7 +62,8 @@ public abstract class InputEventSender { mInputChannel = inputChannel; mMessageQueue = looper.getQueue(); - mSenderPtr = nativeInit(this, inputChannel, mMessageQueue); + mSenderPtr = nativeInit(new WeakReference<InputEventSender>(this), + inputChannel, mMessageQueue); mCloseGuard.open("dispose"); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 6207faa..3b06da7 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6889,7 +6889,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Adds the children of a given View for accessibility. Since some Views are * not important for accessibility the children for accessibility are not - * necessarily direct children of the riew, rather they are the first level of + * necessarily direct children of the view, rather they are the first level of * descendants important for accessibility. * * @param children The list of children for accessibility. diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 725502d..f615e1bc 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -406,10 +406,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager private View[] mChildren; // Number of valid children in the mChildren array, the rest should be null or not // considered as children + private int mChildrenCount; - private boolean mLayoutSuppressed = false; + // Whether layout calls are currently being suppressed, controlled by calls to + // suppressLayout() + boolean mSuppressLayout = false; - private int mChildrenCount; + // Whether any layout calls have actually been suppressed while mSuppressLayout + // has been true. This tracks whether we need to issue a requestLayout() when + // layout is later re-enabled. + private boolean mLayoutCalledWhileSuppressed = false; private static final int ARRAY_INITIAL_CAPACITY = 12; private static final int ARRAY_CAPACITY_INCREMENT = 12; @@ -2564,7 +2570,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager exitHoverTargets(); // In case view is detached while transition is running - mLayoutSuppressed = false; + mLayoutCalledWhileSuppressed = false; // Tear down our drag tracking mDragNotifiedChildren = null; @@ -4525,7 +4531,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager super.layout(l, t, r, b); } else { // record the fact that we noop'd it; request layout when transition finishes - mLayoutSuppressed = true; + mLayoutCalledWhileSuppressed = true; } } @@ -5201,9 +5207,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @Override public void endTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType) { - if (mLayoutSuppressed && !transition.isChangingLayout()) { + if (mLayoutCalledWhileSuppressed && !transition.isChangingLayout()) { requestLayout(); - mLayoutSuppressed = false; + mLayoutCalledWhileSuppressed = false; } if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) { endViewTransition(view); @@ -5212,6 +5218,24 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager }; /** + * Tells this ViewGroup to suppress all layout() calls until layout + * suppression is disabled with a later call to suppressLayout(false). + * When layout suppression is disabled, a requestLayout() call is sent + * if layout() was attempted while layout was being suppressed. + * + * @hide + */ + public void suppressLayout(boolean suppress) { + mSuppressLayout = suppress; + if (!suppress) { + if (mLayoutCalledWhileSuppressed) { + requestLayout(); + mLayoutCalledWhileSuppressed = false; + } + } + } + + /** * {@inheritDoc} */ @Override diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 9387624..efa8a9e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -197,7 +197,6 @@ public final class ViewRootImpl implements ViewParent, int mHeight; Rect mDirty; final Rect mCurrentDirty = new Rect(); - final Rect mPreviousDirty = new Rect(); boolean mIsAnimating; CompatibilityInfo.Translator mTranslator; @@ -767,10 +766,11 @@ public final class ViewRootImpl implements ViewParent, final boolean translucent = attrs.format != PixelFormat.OPAQUE; mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent); - mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString()); - mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested - = mAttachInfo.mHardwareRenderer != null; - + if (mAttachInfo.mHardwareRenderer != null) { + mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString()); + mAttachInfo.mHardwareAccelerated = + mAttachInfo.mHardwareAccelerationRequested = true; + } } else if (fakeHwAccelerated) { // The window had wanted to use hardware acceleration, but this // is not allowed in its process. By setting this flag, it can @@ -2387,14 +2387,10 @@ public final class ViewRootImpl implements ViewParent, mResizeAlpha = resizeAlpha; mCurrentDirty.set(dirty); - mCurrentDirty.union(mPreviousDirty); - mPreviousDirty.set(dirty); dirty.setEmpty(); - if (attachInfo.mHardwareRenderer.draw(mView, attachInfo, this, - animating ? null : mCurrentDirty)) { - mPreviousDirty.set(0, 0, mWidth, mHeight); - } + attachInfo.mHardwareRenderer.draw(mView, attachInfo, this, + animating ? null : mCurrentDirty); } else { // If we get here with a disabled & requested hardware renderer, something went // wrong (an invalidate posted right before we destroyed the hardware surface @@ -2449,6 +2445,8 @@ public final class ViewRootImpl implements ViewParent, canvas = mSurface.lockCanvas(dirty); + // The dirty rectangle can be modified by Surface.lockCanvas() + //noinspection ConstantConditions if (left != dirty.left || top != dirty.top || right != dirty.right || bottom != dirty.bottom) { attachInfo.mIgnoreDirtyState = true; @@ -3099,8 +3097,7 @@ public final class ViewRootImpl implements ViewParent, boolean inTouchMode = msg.arg2 != 0; ensureTouchModeLocally(inTouchMode); - if (mAttachInfo.mHardwareRenderer != null && - mSurface != null && mSurface.isValid()) { + if (mAttachInfo.mHardwareRenderer != null && mSurface.isValid()){ mFullRedrawNeeded = true; try { mAttachInfo.mHardwareRenderer.initializeIfNeeded( diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 7f9969c..855b6d4 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -325,7 +325,8 @@ public final class InputMethodManager { PendingEvent mPendingEventPool; int mPendingEventPoolSize; - PendingEvent mFirstPendingEvent; + PendingEvent mPendingEventHead; + PendingEvent mPendingEventTail; // ----------------------------------------------------------- @@ -366,18 +367,14 @@ public final class InputMethodManager { if (mBindSequence < 0 || mBindSequence != res.sequence) { Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence + ", given seq=" + res.sequence); - if (res.channel != null) { + if (res.channel != null && res.channel != mCurChannel) { res.channel.dispose(); } return; } - - flushPendingEventsLocked(); + + setInputChannelLocked(res.channel); mCurMethod = res.method; - if (mCurChannel != null) { - mCurChannel.dispose(); - } - mCurChannel = res.channel; mCurId = res.id; mBindSequence = res.sequence; } @@ -719,20 +716,26 @@ public final class InputMethodManager { */ void clearBindingLocked() { clearConnectionLocked(); - flushPendingEventsLocked(); + setInputChannelLocked(null); mBindSequence = -1; mCurId = null; mCurMethod = null; - if (mCurSender != null) { - mCurSender.dispose(); - mCurSender = null; - } - if (mCurChannel != null) { - mCurChannel.dispose(); - mCurChannel = null; + } + + void setInputChannelLocked(InputChannel channel) { + if (mCurChannel != channel) { + if (mCurSender != null) { + flushPendingEventsLocked(); + mCurSender.dispose(); + mCurSender = null; + } + if (mCurChannel != null) { + mCurChannel.dispose(); + } + mCurChannel = channel; } } - + /** * Reset all of the state associated with a served view being connected * to an input method @@ -1174,15 +1177,12 @@ public final class InputMethodManager { if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); if (res != null) { if (res.id != null) { + setInputChannelLocked(res.channel); mBindSequence = res.sequence; mCurMethod = res.method; - if (mCurChannel != null) { - mCurChannel.dispose(); - } - mCurChannel = res.channel; mCurId = res.id; } else { - if (res.channel != null) { + if (res.channel != null && res.channel != mCurChannel) { res.channel.dispose(); } if (mCurMethod == null) { @@ -1655,8 +1655,13 @@ public final class InputMethodManager { private void enqueuePendingEventLocked( long startTime, int seq, String inputMethodId, FinishedEventCallback callback) { PendingEvent p = obtainPendingEventLocked(startTime, seq, inputMethodId, callback); - p.mNext = mFirstPendingEvent; - mFirstPendingEvent = p; + if (mPendingEventTail != null) { + mPendingEventTail.mNext = p; + mPendingEventTail = p; + } else { + mPendingEventHead = p; + mPendingEventTail = p; + } Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, seq, 0, p); msg.setAsynchronous(true); @@ -1664,12 +1669,15 @@ public final class InputMethodManager { } private PendingEvent dequeuePendingEventLocked(int seq) { - PendingEvent p = mFirstPendingEvent; + PendingEvent p = mPendingEventHead; if (p == null) { return null; } if (p.mSeq == seq) { - mFirstPendingEvent = p.mNext; + mPendingEventHead = p.mNext; + if (mPendingEventHead == null) { + mPendingEventTail = null; + } } else { PendingEvent prev; do { @@ -1680,6 +1688,9 @@ public final class InputMethodManager { } } while (p.mSeq != seq); prev.mNext = p.mNext; + if (mPendingEventTail == p) { + mPendingEventTail = prev; + } } p.mNext = null; return p; @@ -1716,25 +1727,13 @@ public final class InputMethodManager { private void flushPendingEventsLocked() { mH.removeMessages(MSG_EVENT_TIMEOUT); - PendingEvent curr, prev, next; - curr = mFirstPendingEvent; - prev = null; - while (curr != null) { - next = curr.mNext; - curr.mNext = prev; - prev = curr; - curr = next; - } - curr = prev; - prev = null; - while (curr != null) { - Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, curr.mSeq, 0, curr); + + PendingEvent p = mPendingEventHead; + while (p != null) { + Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, p.mSeq, 0, p); msg.setAsynchronous(true); mH.sendMessage(msg); - next = curr.mNext; - curr.mNext = prev; - prev = curr; - curr = next; + p = p.mNext; } } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 79fc51e..83e2e79 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -34,6 +34,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.os.StrictMode; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; @@ -2263,8 +2264,13 @@ public class RemoteViews implements Parcelable, Filter { * @param value The value to pass to the method. */ public void setUri(int viewId, String methodName, Uri value) { - // Resolve any filesystem path before sending remotely - value = value.getCanonicalUri(); + if (value != null) { + // Resolve any filesystem path before sending remotely + value = value.getCanonicalUri(); + if (StrictMode.vmFileUriExposureEnabled()) { + value.checkFileUriExposed("RemoteViews.setUri()"); + } + } addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value)); } diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java index 4045497..62afd2e 100644 --- a/core/java/android/widget/ShareActionProvider.java +++ b/core/java/android/widget/ShareActionProvider.java @@ -39,31 +39,26 @@ import com.android.internal.R; * <p> * Here is how to use the action provider with custom backing file in a {@link MenuItem}: * </p> - * <p> * <pre> - * <code> - * // In Activity#onCreateOptionsMenu - * public boolean onCreateOptionsMenu(Menu menu) { - * // Get the menu item. - * MenuItem menuItem = menu.findItem(R.id.my_menu_item); - * // Get the provider and hold onto it to set/change the share intent. - * mShareActionProvider = (ShareActionProvider) menuItem.getActionProvider(); - * // Set history different from the default before getting the action - * // view since a call to {@link MenuItem#getActionView() MenuItem.getActionView()} calls - * // {@link ActionProvider#onCreateActionView()} which uses the backing file name. Omit this - * // line if using the default share history file is desired. - * mShareActionProvider.setShareHistoryFileName("custom_share_history.xml"); - * . . . - * } + * // In Activity#onCreateOptionsMenu + * public boolean onCreateOptionsMenu(Menu menu) { + * // Get the menu item. + * MenuItem menuItem = menu.findItem(R.id.my_menu_item); + * // Get the provider and hold onto it to set/change the share intent. + * mShareActionProvider = (ShareActionProvider) menuItem.getActionProvider(); + * // Set history different from the default before getting the action + * // view since a call to {@link MenuItem#getActionView() MenuItem.getActionView()} calls + * // {@link ActionProvider#onCreateActionView()} which uses the backing file name. Omit this + * // line if using the default share history file is desired. + * mShareActionProvider.setShareHistoryFileName("custom_share_history.xml"); + * . . . + * } * - * // Somewhere in the application. - * public void doShare(Intent shareIntent) { - * // When you want to share set the share intent. - * mShareActionProvider.setShareIntent(shareIntent); - * } - * </pre> - * </code> - * </p> + * // Somewhere in the application. + * public void doShare(Intent shareIntent) { + * // When you want to share set the share intent. + * mShareActionProvider.setShareIntent(shareIntent); + * }</pre> * <p> * <strong>Note:</strong> While the sample snippet demonstrates how to use this provider * in the context of a menu item, the use of the provider is not limited to menu items. @@ -245,9 +240,9 @@ public class ShareActionProvider extends ActionProvider { * call {@link android.app.Activity#invalidateOptionsMenu()} to recreate the * action view. You should <strong>not</strong> call * {@link android.app.Activity#invalidateOptionsMenu()} from - * {@link android.app.Activity#onCreateOptionsMenu(Menu)}." - * <p> - * <code> + * {@link android.app.Activity#onCreateOptionsMenu(Menu)}. + * </p> + * <pre> * private void doShare(Intent intent) { * if (IMAGE.equals(intent.getMimeType())) { * mShareActionProvider.setHistoryFileName(SHARE_IMAGE_HISTORY_FILE_NAME); @@ -256,9 +251,7 @@ public class ShareActionProvider extends ActionProvider { * } * mShareActionProvider.setIntent(intent); * invalidateOptionsMenu(); - * } - * <code> - * + * }</pre> * @param shareHistoryFile The share history file name. */ public void setShareHistoryFileName(String shareHistoryFile) { @@ -269,16 +262,11 @@ public class ShareActionProvider extends ActionProvider { /** * Sets an intent with information about the share action. Here is a * sample for constructing a share intent: - * <p> * <pre> - * <code> - * Intent shareIntent = new Intent(Intent.ACTION_SEND); - * shareIntent.setType("image/*"); - * Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg")); - * shareIntent.putExtra(Intent.EXTRA_STREAM, uri.toString()); - * </pre> - * </code> - * </p> + * Intent shareIntent = new Intent(Intent.ACTION_SEND); + * shareIntent.setType("image/*"); + * Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg")); + * shareIntent.putExtra(Intent.EXTRA_STREAM, uri.toString());</pre> * * @param shareIntent The share intent. * |