diff options
Diffstat (limited to 'core/java')
67 files changed, 1707 insertions, 613 deletions
diff --git a/core/java/android/animation/RectEvaluator.java b/core/java/android/animation/RectEvaluator.java new file mode 100644 index 0000000..28d496b --- /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>Rect</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/ActionBar.java b/core/java/android/app/ActionBar.java index 3602fc4..c4ddf1f 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -695,6 +695,86 @@ public abstract class ActionBar { public boolean isTitleTruncated() { return false; } /** + * Set an alternate drawable to display next to the icon/logo/title + * when {@link #DISPLAY_HOME_AS_UP} is enabled. This can be useful if you are using + * this mode to display an alternate selection for up navigation, such as a sliding drawer. + * + * <p>If you pass <code>null</code> to this method, the default drawable from the theme + * will be used.</p> + * + * <p>If you implement alternate or intermediate behavior around Up, you should also + * call {@link #setHomeActionContentDescription(int) setHomeActionContentDescription()} + * to provide a correct description of the action for accessibility support.</p> + * + * @param indicator A drawable to use for the up indicator, or null to use the theme's default + * + * @see #setDisplayOptions(int, int) + * @see #setDisplayHomeAsUpEnabled(boolean) + * @see #setHomeActionContentDescription(int) + */ + public void setHomeAsUpIndicator(Drawable indicator) { } + + /** + * Set an alternate drawable to display next to the icon/logo/title + * when {@link #DISPLAY_HOME_AS_UP} is enabled. This can be useful if you are using + * this mode to display an alternate selection for up navigation, such as a sliding drawer. + * + * <p>If you pass <code>0</code> to this method, the default drawable from the theme + * will be used.</p> + * + * <p>If you implement alternate or intermediate behavior around Up, you should also + * call {@link #setHomeActionContentDescription(int) setHomeActionContentDescription()} + * to provide a correct description of the action for accessibility support.</p> + * + * @param resId Resource ID of a drawable to use for the up indicator, or null + * to use the theme's default + * + * @see #setDisplayOptions(int, int) + * @see #setDisplayHomeAsUpEnabled(boolean) + * @see #setHomeActionContentDescription(int) + */ + public void setHomeAsUpIndicator(int resId) { } + + /** + * Set an alternate description for the Home/Up action, when enabled. + * + * <p>This description is commonly used for accessibility/screen readers when + * the Home action is enabled. (See {@link #setDisplayHomeAsUpEnabled(boolean)}.) + * Examples of this are, "Navigate Home" or "Navigate Up" depending on the + * {@link #DISPLAY_HOME_AS_UP} display option. If you have changed the home-as-up + * indicator using {@link #setHomeAsUpIndicator(int)} to indicate more specific + * functionality such as a sliding drawer, you should also set this to accurately + * describe the action.</p> + * + * <p>Setting this to <code>null</code> will use the system default description.</p> + * + * @param description New description for the Home action when enabled + * @see #setHomeAsUpIndicator(int) + * @see #setHomeAsUpIndicator(android.graphics.drawable.Drawable) + */ + public void setHomeActionContentDescription(CharSequence description) { } + + /** + * Set an alternate description for the Home/Up action, when enabled. + * + * <p>This description is commonly used for accessibility/screen readers when + * the Home action is enabled. (See {@link #setDisplayHomeAsUpEnabled(boolean)}.) + * Examples of this are, "Navigate Home" or "Navigate Up" depending on the + * {@link #DISPLAY_HOME_AS_UP} display option. If you have changed the home-as-up + * indicator using {@link #setHomeAsUpIndicator(int)} to indicate more specific + * functionality such as a sliding drawer, you should also set this to accurately + * describe the action.</p> + * + * <p>Setting this to <code>0</code> will use the system default description.</p> + * + * @param resId Resource ID of a string to use as the new description + * for the Home action when enabled + * @see #setHomeAsUpIndicator(int) + * @see #setHomeAsUpIndicator(android.graphics.drawable.Drawable) + */ + public void setHomeActionContentDescription(int resId) { } + + /** * Listener interface for ActionBar navigation events. */ public interface OnNavigationListener { 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/ActivityThread.java b/core/java/android/app/ActivityThread.java index ae0671b..68a2397 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -86,6 +86,7 @@ import android.view.Window; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.renderscript.RenderScript; +import android.security.AndroidKeyStoreProvider; import com.android.internal.os.BinderInternal; import com.android.internal.os.RuntimeInit; @@ -101,6 +102,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.net.InetAddress; +import java.security.Security; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -5068,6 +5070,8 @@ public final class ActivityThread { // Set the reporter for event logging in libcore EventLogger.setReporter(new EventLoggingReporter()); + Security.addProvider(new AndroidKeyStoreProvider()); + Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); 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/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 3d9b2ae..92ec3ad 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -17,12 +17,12 @@ package android.app; -import android.app.INotificationListener; import android.app.ITransientNotification; +import android.service.notification.StatusBarNotification; import android.app.Notification; +import android.content.ComponentName; import android.content.Intent; - -import com.android.internal.statusbar.StatusBarNotification; +import android.service.notification.INotificationListener; /** {@hide} */ interface INotificationManager @@ -41,7 +41,9 @@ interface INotificationManager StatusBarNotification[] getActiveNotifications(String callingPkg); StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count); - void registerListener(in INotificationListener listener, String pkg, int userid); + void registerListener(in INotificationListener listener, in ComponentName component, int userid); void unregisterListener(in INotificationListener listener, int userid); -} + void clearNotificationFromListener(in INotificationListener token, String pkg, String tag, int id); + void clearAllNotificationsFromListener(in INotificationListener token); +}
\ No newline at end of file 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/Notification.java b/core/java/android/app/Notification.java index ebca041..a7543a8 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1603,7 +1603,7 @@ public class Notification implements Parcelable n.defaults = mDefaults; n.flags = mFlags; n.bigContentView = makeBigContentView(); - if (mLedOnMs != 0 && mLedOffMs != 0) { + if (mLedOnMs != 0 || mLedOffMs != 0) { n.flags |= FLAG_SHOW_LIGHTS; } if ((mDefaults & DEFAULT_LIGHTS) != 0) { 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..384aed8 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); @@ -1017,25 +1036,10 @@ public class PackageParser { return null; } } else if (tagName.equals("uses-permission")) { - sa = res.obtainAttributes(attrs, - com.android.internal.R.styleable.AndroidManifestUsesPermission); - - // Note: don't allow this value to be a reference to a resource - // that may change. - String name = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestUsesPermission_name); - boolean required = sa.getBoolean( - com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true); - - sa.recycle(); - - if (name != null && !pkg.requestedPermissions.contains(name)) { - pkg.requestedPermissions.add(name.intern()); - pkg.requestedPermissionsRequired.add(required ? Boolean.TRUE : Boolean.FALSE); + if (!parseUsesPermission(pkg, res, parser, attrs, outError)) { + return null; } - XmlUtils.skipCurrentTag(parser); - } else if (tagName.equals("uses-configuration")) { ConfigurationInfo cPref = new ConfigurationInfo(); sa = res.obtainAttributes(attrs, @@ -1379,9 +1383,53 @@ 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; } + private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser, + AttributeSet attrs, String[] outError) + throws XmlPullParserException, IOException { + TypedArray sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestUsesPermission); + + // Note: don't allow this value to be a reference to a resource + // that may change. + String name = sa.getNonResourceString( + com.android.internal.R.styleable.AndroidManifestUsesPermission_name); + boolean required = sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true); + + sa.recycle(); + + if (name != null) { + int index = pkg.requestedPermissions.indexOf(name); + if (index == -1) { + pkg.requestedPermissions.add(name.intern()); + pkg.requestedPermissionsRequired.add(required ? Boolean.TRUE : Boolean.FALSE); + } else { + if (pkg.requestedPermissionsRequired.get(index) != required) { + outError[0] = "conflicting <uses-permission> entries"; + mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; + return false; + } + } + } + + XmlUtils.skipCurrentTag(parser); + return true; + } + private static String buildClassName(String pkg, CharSequence clsSeq, String[] outError) { if (clsSeq == null || clsSeq.length() <= 0) { 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/Binder.java b/core/java/android/os/Binder.java index 16b4835..e9e7551 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -152,7 +152,15 @@ public class Binder implements IBinder { * not return until the current process is exiting. */ public static final native void joinThreadPool(); - + + /** + * Returns true if the specified interface is a proxy. + * @hide + */ + public static final boolean isProxy(IInterface iface) { + return iface.asBinder() != iface; + } + /** * Default constructor initializes the object. */ diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java index 02135bc..38f4d5e 100644 --- a/core/java/android/os/Looper.java +++ b/core/java/android/os/Looper.java @@ -50,7 +50,7 @@ import android.util.PrefixPrinter; * } * }</pre> */ -public class Looper { +public final class Looper { private static final String TAG = "Looper"; // sThreadLocal.get() will return null unless you've called prepare(). @@ -223,7 +223,7 @@ public class Looper { * * @hide */ - public final int postSyncBarrier() { + public int postSyncBarrier() { return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis()); } @@ -238,7 +238,7 @@ public class Looper { * * @hide */ - public final void removeSyncBarrier(int token) { + public void removeSyncBarrier(int token) { mQueue.removeSyncBarrier(token); } diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java index 222578a..e0d40c9 100644 --- a/core/java/android/os/MessageQueue.java +++ b/core/java/android/os/MessageQueue.java @@ -29,7 +29,7 @@ import java.util.ArrayList; * <p>You can retrieve the MessageQueue for the current thread with * {@link Looper#myQueue() Looper.myQueue()}. */ -public class MessageQueue { +public final class MessageQueue { // True if the message queue can be quit. private final boolean mQuitAllowed; @@ -78,7 +78,7 @@ public class MessageQueue { * * @param handler The IdleHandler to be added. */ - public final void addIdleHandler(IdleHandler handler) { + public void addIdleHandler(IdleHandler handler) { if (handler == null) { throw new NullPointerException("Can't add a null IdleHandler"); } @@ -94,7 +94,7 @@ public class MessageQueue { * * @param handler The IdleHandler to be removed. */ - public final void removeIdleHandler(IdleHandler handler) { + public void removeIdleHandler(IdleHandler handler) { synchronized (this) { mIdleHandlers.remove(handler); } @@ -121,7 +121,7 @@ public class MessageQueue { } } - final Message next() { + Message next() { int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; @@ -218,7 +218,7 @@ public class MessageQueue { } } - final void quit() { + void quit() { if (!mQuitAllowed) { throw new RuntimeException("Main thread not allowed to quit."); } @@ -232,7 +232,7 @@ public class MessageQueue { nativeWake(mPtr); } - final int enqueueSyncBarrier(long when) { + int enqueueSyncBarrier(long when) { // Enqueue a new sync barrier token. // We don't need to wake the queue because the purpose of a barrier is to stall it. synchronized (this) { @@ -259,7 +259,7 @@ public class MessageQueue { } } - final void removeSyncBarrier(int token) { + void removeSyncBarrier(int token) { // Remove a sync barrier token from the queue. // If the queue is no longer stalled by a barrier then wake it. final boolean needWake; @@ -288,7 +288,7 @@ public class MessageQueue { } } - final boolean enqueueMessage(Message msg, long when) { + boolean enqueueMessage(Message msg, long when) { if (msg.isInUse()) { throw new AndroidRuntimeException(msg + " This message is already in use."); } @@ -338,7 +338,7 @@ public class MessageQueue { return true; } - final boolean hasMessages(Handler h, int what, Object object) { + boolean hasMessages(Handler h, int what, Object object) { if (h == null) { return false; } @@ -355,7 +355,7 @@ public class MessageQueue { } } - final boolean hasMessages(Handler h, Runnable r, Object object) { + boolean hasMessages(Handler h, Runnable r, Object object) { if (h == null) { return false; } @@ -372,7 +372,7 @@ public class MessageQueue { } } - final void removeMessages(Handler h, int what, Object object) { + void removeMessages(Handler h, int what, Object object) { if (h == null) { return; } @@ -406,7 +406,7 @@ public class MessageQueue { } } - final void removeMessages(Handler h, Runnable r, Object object) { + void removeMessages(Handler h, Runnable r, Object object) { if (h == null || r == null) { return; } @@ -440,7 +440,7 @@ public class MessageQueue { } } - final void removeCallbacksAndMessages(Handler h, Object object) { + void removeCallbacksAndMessages(Handler h, Object object) { if (h == null) { return; } 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/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 66083c8..367d576 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -3796,50 +3796,11 @@ public final class ContactsContract { * Columns in the Data_Usage_Stat table */ protected interface DataUsageStatColumns { - /** What the referenced {@link Data} was used for. - * @see DataUsageStatColumns#USAGE_TYPE_CALL - * @see DataUsageStatColumns#USAGE_TYPE_LONG_TEXT - * @see DataUsageStatColumns#USAGE_TYPE_SHORT_TEXT - */ - public static final String USAGE_TYPE = "usage_type"; - /** The last time (in milliseconds) this {@link Data} was used. */ public static final String LAST_TIME_USED = "last_time_used"; - /** The number of times the referenced {@link Data} has been used for the purpose described - * in {@link DataUsageStatColumns#USAGE_TYPE}. - */ + /** The number of times the referenced {@link Data} has been used. */ public static final String TIMES_USED = "times_used"; - - /** - * Integer value for USAGE_TYPE. - * This type of usage refers to voice interaction, which includes phone calls, voice chat, - * and video chat. - * - * @see DataUsageFeedback#USAGE_TYPE - * @see DataUsageStatColumns#USAGE_TYPE - */ - public static final int USAGE_TYPE_CALL = 0; - - /** - * Integer value for USAGE_TYPE. - * This type of usage refers to text interaction involving longer messages, which includes - * email. - * - * @see DataUsageFeedback#USAGE_TYPE - * @see DataUsageStatColumns#USAGE_TYPE - */ - public static final int USAGE_TYPE_LONG_TEXT = 1; - - /** - * Integer value for USAGE_TYPE. - * This type of usage for text interaction involving shorter messages, which includes SMS - * and text chat with email addresses. - * - * @see DataUsageFeedback#USAGE_TYPE - * @see DataUsageStatColumns#USAGE_TYPE - */ - public static final int USAGE_TYPE_SHORT_TEXT = 2; } /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 03ee9eb..88ee414 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -655,6 +655,22 @@ public final class Settings { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS"; + /** + * Activity Action: Show Notification listener settings. + * <p> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + * @see android.service.notification.NotificationListenerService + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_NOTIFICATION_LISTENER_SETTINGS + = "android.settings.NOTIFICATION_LISTENER_SETTINGS"; + // End of Intent actions for Settings /** @@ -5398,6 +5414,20 @@ public final class Settings { public static final String CERT_PIN_UPDATE_METADATA_URL = "cert_pin_metadata_url"; /** + * URL for intent firewall updates + * @hide + */ + public static final String INTENT_FIREWALL_UPDATE_CONTENT_URL = + "intent_firewall_content_url"; + + /** + * URL for intent firewall update metadata + * @hide + */ + public static final String INTENT_FIREWALL_UPDATE_METADATA_URL = + "intent_firewall_metadata_url"; + + /** * SELinux enforcement status. If 0, permissive; if 1, enforcing. * @hide */ 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/app/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index f010a2a..425fdc1 100644 --- a/core/java/android/app/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -14,9 +14,9 @@ * limitations under the License. */ -package android.app; +package android.service.notification; -import com.android.internal.statusbar.StatusBarNotification; +import android.service.notification.StatusBarNotification; /** @hide */ oneway interface INotificationListener diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java new file mode 100644 index 0000000..86bab2a --- /dev/null +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -0,0 +1,138 @@ +/* + * 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.service.notification; + +import android.annotation.SdkConstant; +import android.app.INotificationManager; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import android.os.ServiceManager; +import android.util.Log; + +public abstract class NotificationListenerService extends Service { + // TAG = "NotificationListenerService[MySubclass]" + private final String TAG = NotificationListenerService.class.getSimpleName() + + "[" + getClass().getSimpleName() + "]"; + + private INotificationListenerWrapper mWrapper = null; + + private INotificationManager mNoMan; + + /** + * The {@link Intent} that must be declared as handled by the service. + */ + @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) + public static final String SERVICE_INTERFACE + = "android.service.notification.NotificationListenerService"; + + /** + * Implement this method to learn about new notifications as they are posted by apps. + * + * @param sbn A data structure encapsulating the original {@link android.app.Notification} + * object as well as its identifying information (tag and id) and source + * (package name). + */ + public abstract void onNotificationPosted(StatusBarNotification sbn); + + /** + * Implement this method to learn when notifications are removed. + * <P> + * This might occur because the user has dismissed the notification using system UI (or another + * notification listener) or because the app has withdrawn the notification. + * + * @param sbn A data structure encapsulating the original {@link android.app.Notification} + * object as well as its identifying information (tag and id) and source + * (package name). + */ + public abstract void onNotificationRemoved(StatusBarNotification sbn); + + private final INotificationManager getNotificationInterface() { + if (mNoMan == null) { + mNoMan = INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + } + return mNoMan; + } + + /** + * Inform the notification manager about dismissal of a single notification. + * <p> + * Use this if your listener has a user interface that allows the user to dismiss individual + * notifications, similar to the behavior of Android's status bar and notification panel. + * It should be called after the user dismisses a single notification using your UI; + * upon being informed, the notification manager will actually remove the notification + * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback. + * <P> + * <b>Note:</b> If your listener allows the user to fire a notification's + * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call + * this method at that time <i>if</i> the Notification in question has the + * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set. + * + * @param pkg Package of the notifying app. + * @param tag Tag of the notification as specified by the notifying app in + * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}. + * @param id ID of the notification as specified by the notifying app in + * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}. + */ + public final void clearNotification(String pkg, String tag, int id) { + try { + getNotificationInterface().clearNotificationFromListener(mWrapper, pkg, tag, id); + } catch (android.os.RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + } + } + + /** + * Inform the notification manager about dismissal of all notifications. + * <p> + * Use this if your listener has a user interface that allows the user to dismiss all + * notifications, similar to the behavior of Android's status bar and notification panel. + * It should be called after the user invokes the "dismiss all" function of your UI; + * upon being informed, the notification manager will actually remove all active notifications + * and you will get multiple {@link #onNotificationRemoved(StatusBarNotification)} callbacks. + * + * {@see #clearNotification(String, String, int)} + */ + public final void clearAllNotifications() { + try { + getNotificationInterface().clearAllNotificationsFromListener(mWrapper); + } catch (android.os.RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + } + } + + @Override + public IBinder onBind(Intent intent) { + if (mWrapper == null) { + mWrapper = new INotificationListenerWrapper(); + } + return mWrapper; + } + + private class INotificationListenerWrapper extends INotificationListener.Stub { + @Override + public void onNotificationPosted(StatusBarNotification sbn) { + NotificationListenerService.this.onNotificationPosted(sbn); + } + @Override + public void onNotificationRemoved(StatusBarNotification sbn) { + NotificationListenerService.this.onNotificationRemoved(sbn); + } + } +} diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.aidl b/core/java/android/service/notification/StatusBarNotification.aidl index bd9e89c..ba81972 100644 --- a/core/java/com/android/internal/statusbar/StatusBarNotification.aidl +++ b/core/java/android/service/notification/StatusBarNotification.aidl @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.statusbar; +package android.service.notification; parcelable StatusBarNotification; diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 23e87fc..ef5f8c4 100644 --- a/core/java/com/android/internal/statusbar/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.statusbar; +package android.service.notification; import android.app.Notification; import android.os.Parcel; @@ -23,34 +23,54 @@ import android.os.UserHandle; /** * Class encapsulating a Notification. Sent by the NotificationManagerService to clients including - * the IStatusBar (in System UI). + * the status bar and any {@link android.service.notification.NotificationListenerService}s. */ public class StatusBarNotification implements Parcelable { + /** The package of the app that posted the notification. */ public final String pkg; - public final String basePkg; + /** The id supplied to {@link android.app.NotificationManager#notify}. */ public final int id; + /** The tag supplied to {@link android.app.NotificationManager#notify}, or null if no tag + * was specified. */ public final String tag; + + /** The notifying app's calling uid. @hide */ public final int uid; + /** The notifying app's base package. @hide */ + public final String basePkg; + /** @hide */ public final int initialPid; // TODO: make this field private and move callers to an accessor that // ensures sourceUser is applied. + + /** The {@link android.app.Notification} supplied to + * {@link android.app.NotificationManager#notify}. */ public final Notification notification; - public final int score; + /** The {@link android.os.UserHandle} for whom this notification is intended. */ public final UserHandle user; + /** The time (in {@link System#currentTimeMillis} time) the notification was posted, + * which may be different than {@link android.app.Notification#when}. + */ public final long postTime; - /** This is temporarily needed for the JB MR1 PDK. */ + /** @hide */ + public final int score; + + /** This is temporarily needed for the JB MR1 PDK. + * @hide */ @Deprecated public StatusBarNotification(String pkg, int id, String tag, int uid, int initialPid, int score, Notification notification) { this(pkg, id, tag, uid, initialPid, score, notification, UserHandle.OWNER); } + /** @hide */ public StatusBarNotification(String pkg, int id, String tag, int uid, int initialPid, int score, Notification notification, UserHandle user) { this(pkg, null, id, tag, uid, initialPid, score, notification, user); } + /** @hide */ public StatusBarNotification(String pkg, String basePkg, int id, String tag, int uid, int initialPid, int score, Notification notification, UserHandle user) { this(pkg, basePkg, id, tag, uid, initialPid, score, notification, user, @@ -147,10 +167,17 @@ public class StatusBarNotification implements Parcelable { this.score, this.notification); } + /** Convenience method to check the notification's flags for + * {@link Notification#FLAG_ONGOING_EVENT}. + */ public boolean isOngoing() { return (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0; } + /** Convenience method to check the notification's flags for + * either {@link Notification#FLAG_ONGOING_EVENT} or + * {@link Notification#FLAG_NO_CLEAR}. + */ public boolean isClearable() { return ((notification.flags & Notification.FLAG_ONGOING_EVENT) == 0) && ((notification.flags & Notification.FLAG_NO_CLEAR) == 0); diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index f813df3..c497e35 100644 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -31,6 +31,7 @@ import java.util.Locale; import java.util.TimeZone; import java.text.SimpleDateFormat; +import libcore.icu.ICU; import libcore.icu.LocaleData; /** @@ -43,6 +44,9 @@ import libcore.icu.LocaleData; * for both formatting and parsing dates. For the canonical documentation * of format strings, see {@link java.text.SimpleDateFormat}. * + * <p>In cases where the system does not provide a suitable pattern, + * this class offers the {@link #getBestDateTimePattern} method. + * * <p>The {@code format} methods in this class implement a subset of Unicode * <a href="http://www.unicode.org/reports/tr35/#Date_Format_Patterns">UTS #35</a> patterns. * The subset currently supported by this class includes the following format characters: @@ -164,6 +168,37 @@ public class DateFormat { } /** + * Returns the best possible localized form of the given skeleton for the given + * locale. A skeleton is similar to, and uses the same format characters as, a Unicode + * <a href="http://www.unicode.org/reports/tr35/#Date_Format_Patterns">UTS #35</a> + * pattern. + * + * <p>One difference is that order is irrelevant. For example, "MMMMd" will return + * "MMMM d" in the {@code en_US} locale, but "d. MMMM" in the {@code de_CH} locale. + * + * <p>Note also in that second example that the necessary punctuation for German was + * added. For the same input in {@code es_ES}, we'd have even more extra text: + * "d 'de' MMMM". + * + * <p>This method will automatically correct for grammatical necessity. Given the + * same "MMMMd" input, this method will return "d LLLL" in the {@code fa_IR} locale, + * where stand-alone months are necessary. Lengths are preserved where meaningful, + * so "Md" would give a different result to "MMMd", say, except in a locale such as + * {@code ja_JP} where there is only one length of month. + * + * <p>This method will only return patterns that are in CLDR, and is useful whenever + * you know what elements you want in your format string but don't want to make your + * code specific to any one locale. + * + * @param locale the locale into which the skeleton should be localized + * @param skeleton a skeleton as described above + * @return a string pattern suitable for use with {@link java.text.SimpleDateFormat}. + */ + public static String getBestDateTimePattern(Locale locale, String skeleton) { + return ICU.getBestDateTimePattern(skeleton, locale.toString()); + } + + /** * Returns a {@link java.text.DateFormat} object that can format the time according * to the current locale and the user's 12-/24-hour clock preference. * @param context the application context 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/InputChannel.java b/core/java/android/view/InputChannel.java index a797176..40ee1ad 100644 --- a/core/java/android/view/InputChannel.java +++ b/core/java/android/view/InputChannel.java @@ -54,6 +54,7 @@ public final class InputChannel implements Parcelable { private native void nativeTransferTo(InputChannel other); private native void nativeReadFromParcel(Parcel parcel); private native void nativeWriteToParcel(Parcel parcel); + private native void nativeDup(InputChannel target); private native String nativeGetName(); @@ -64,7 +65,7 @@ public final class InputChannel implements Parcelable { */ public InputChannel() { } - + @Override protected void finalize() throws Throwable { try { @@ -125,6 +126,15 @@ public final class InputChannel implements Parcelable { nativeTransferTo(outParameter); } + /** + * Duplicates the input channel. + */ + public InputChannel dup() { + InputChannel target = new InputChannel(); + nativeDup(target); + return target; + } + @Override public int describeContents() { return Parcelable.CONTENTS_FILE_DESCRIPTOR; diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index dd523d2..2595ee5 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -371,8 +371,8 @@ public final class InputDevice implements Parcelable { if (axis < 0) { break; } - addMotionRange(axis, in.readInt(), - in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat()); + addMotionRange(axis, in.readInt(), in.readFloat(), in.readFloat(), in.readFloat(), + in.readFloat(), in.readFloat()); } } @@ -584,8 +584,8 @@ public final class InputDevice implements Parcelable { // Called from native code. private void addMotionRange(int axis, int source, - float min, float max, float flat, float fuzz) { - mMotionRanges.add(new MotionRange(axis, source, min, max, flat, fuzz)); + float min, float max, float flat, float fuzz, float resolution) { + mMotionRanges.add(new MotionRange(axis, source, min, max, flat, fuzz, resolution)); } /** @@ -625,14 +625,17 @@ public final class InputDevice implements Parcelable { private float mMax; private float mFlat; private float mFuzz; + private float mResolution; - private MotionRange(int axis, int source, float min, float max, float flat, float fuzz) { + private MotionRange(int axis, int source, float min, float max, float flat, float fuzz, + float resolution) { mAxis = axis; mSource = source; mMin = min; mMax = max; mFlat = flat; mFuzz = fuzz; + mResolution = resolution; } /** @@ -711,6 +714,14 @@ public final class InputDevice implements Parcelable { public float getFuzz() { return mFuzz; } + + /** + * Gets the resolution for input device measurements with respect to this axis. + * @return The resolution in units per millimeter, or units per radian for rotational axes. + */ + public float getResolution() { + return mResolution; + } } @Override @@ -734,6 +745,7 @@ public final class InputDevice implements Parcelable { out.writeFloat(range.mMax); out.writeFloat(range.mFlat); out.writeFloat(range.mFuzz); + out.writeFloat(range.mResolution); } out.writeInt(-1); } @@ -788,6 +800,7 @@ public final class InputDevice implements Parcelable { description.append(" max=").append(range.mMax); description.append(" flat=").append(range.mFlat); description.append(" fuzz=").append(range.mFuzz); + description.append(" resolution=").append(range.mResolution); description.append("\n"); } return description.toString(); 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 a5b3c8f..7c82f7e 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. @@ -9485,17 +9485,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <p>Sets the opacity of the view. This is a value from 0 to 1, where 0 means the view is * completely transparent and 1 means the view is completely opaque.</p> * + * <p> Note that setting alpha to a translucent value (0 < alpha < 1) can have significant + * performance implications, especially for large views. It is best to use the alpha property + * sparingly and transiently, as in the case of fading animations.</p> + * + * <p>For a view with a frequently changing alpha, such as during a fading animation, it is + * strongly recommended for performance reasons to either override + * {@link #hasOverlappingRendering()} to return false if appropriate, or setting a + * {@link #setLayerType(int, android.graphics.Paint) layer type} on the view.</p> + * * <p>If this view overrides {@link #onSetAlpha(int)} to return true, then this view is - * responsible for applying the opacity itself. Otherwise, calling this method is - * equivalent to calling {@link #setLayerType(int, android.graphics.Paint)} and - * setting a hardware layer.</p> + * responsible for applying the opacity itself.</p> * - * <p>Note that setting alpha to a translucent value (0 < alpha < 1) may have - * performance implications. It is generally best to use the alpha property sparingly and - * transiently, as in the case of fading animations.</p> + * <p>Note that if the view is backed by a + * {@link #setLayerType(int, android.graphics.Paint) layer} and is associated with a + * {@link #setLayerPaint(android.graphics.Paint) layer paint}, setting an alpha value less than + * 1.0 will supercede the alpha of the layer paint.</p> * * @param alpha The opacity of the view. * + * @see #hasOverlappingRendering() * @see #setLayerType(int, android.graphics.Paint) * * @attr ref android.R.styleable#View_alpha @@ -12098,7 +12107,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, //System.out.println("Attached! " + this); mAttachInfo = info; if (mOverlay != null) { - mOverlay.dispatchAttachedToWindow(info, visibility); + mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility); } mWindowAttachCount++; // We will need to evaluate the drawable state at least once. @@ -12169,7 +12178,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mAttachInfo = null; if (mOverlay != null) { - mOverlay.dispatchDetachedFromWindow(); + mOverlay.getOverlayView().dispatchDetachedFromWindow(); } } @@ -12365,13 +12374,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * </ul> * * <p>If this view has an alpha value set to < 1.0 by calling - * {@link #setAlpha(float)}, the alpha value of the layer's paint is replaced by - * this view's alpha value. Calling {@link #setAlpha(float)} is therefore - * equivalent to setting a hardware layer on this view and providing a paint with - * the desired alpha value.</p> + * {@link #setAlpha(float)}, the alpha value of the layer's paint is superceded + * by this view's alpha value.</p> * - * <p>Refer to the documentation of {@link #LAYER_TYPE_NONE disabled}, - * {@link #LAYER_TYPE_SOFTWARE software} and {@link #LAYER_TYPE_HARDWARE hardware} + * <p>Refer to the documentation of {@link #LAYER_TYPE_NONE}, + * {@link #LAYER_TYPE_SOFTWARE} and {@link #LAYER_TYPE_HARDWARE} * for more information on when and how to use layers.</p> * * @param layerType The type of layer to use with this view, must be one of @@ -12441,11 +12448,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <li>{@link android.graphics.Paint#getColorFilter() Color filter}</li> * </ul> * - * <p>If this view has an alpha value set to < 1.0 by calling - * {@link #setAlpha(float)}, the alpha value of the layer's paint is replaced by - * this view's alpha value. Calling {@link #setAlpha(float)} is therefore - * equivalent to setting a hardware layer on this view and providing a paint with - * the desired alpha value.</p> + * <p>If this view has an alpha value set to < 1.0 by calling {@link #setAlpha(float)}, the + * alpha value of the layer's paint is superceded by this view's alpha value.</p> * * @param paint The paint used to compose the layer. This argument is optional * and can be null. It is ignored when the layer type is @@ -12827,7 +12831,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { dispatchDraw(canvas); if (mOverlay != null && !mOverlay.isEmpty()) { - mOverlay.draw(canvas); + mOverlay.getOverlayView().draw(canvas); } } else { draw(canvas); @@ -13143,7 +13147,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchDraw(canvas); if (mOverlay != null && !mOverlay.isEmpty()) { - mOverlay.draw(canvas); + mOverlay.getOverlayView().draw(canvas); } } else { draw(canvas); @@ -13901,7 +13905,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, onDrawScrollBars(canvas); if (mOverlay != null && !mOverlay.isEmpty()) { - mOverlay.dispatchDraw(canvas); + mOverlay.getOverlayView().dispatchDraw(canvas); } // we're done... @@ -14045,34 +14049,28 @@ public class View implements Drawable.Callback, KeyEvent.Callback, onDrawScrollBars(canvas); if (mOverlay != null && !mOverlay.isEmpty()) { - mOverlay.dispatchDraw(canvas); + mOverlay.getOverlayView().dispatchDraw(canvas); } } /** - * Called by the addToOverlay() methods to create, attach, and size the overlay as necessary + * Returns the overlay for this view, creating it if it does not yet exist. + * Adding drawables to the overlay will cause them to be displayed whenever + * the view itself is redrawn. Objects in the overlay should be actively + * managed: remove them when they should not be displayed anymore. The + * overlay will always have the same size as its host view. + * + * <p>Note: Overlays do not currently work correctly with {@link + * SurfaceView} or {@link TextureView}; contents in overlays for these + * types of views may not display correctly.</p> + * + * @return The ViewOverlay object for this view. + * @see ViewOverlay */ - private void setupOverlay() { + public ViewOverlay getOverlay() { if (mOverlay == null) { mOverlay = new ViewOverlay(mContext, this); - mOverlay.mAttachInfo = mAttachInfo; - mOverlay.setRight(mRight); - mOverlay.setBottom(mBottom); } - } - - /** - * Returns the overlay for this view, creating it if it does not yet exist. Adding drawables - * and/or views to the overlay will cause them to be displayed whenever the view itself is - * redrawn. Objects in the overlay should be actively managed: remove them when they should - * not be displayed anymore and invalidate this view appropriately when overlay drawables - * change. The overlay will always have the same size as its host view. - * - * @return The Overlay object for this view. - * @see Overlay - */ - public Overlay getOverlay() { - setupOverlay(); return mOverlay; } @@ -14356,8 +14354,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private void sizeChange(int newWidth, int newHeight, int oldWidth, int oldHeight) { onSizeChanged(newWidth, newHeight, oldWidth, oldHeight); if (mOverlay != null) { - mOverlay.setRight(mRight); - mOverlay.setBottom(mBottom); + mOverlay.getOverlayView().setRight(newWidth); + mOverlay.getOverlayView().setBottom(newHeight); } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 725502d..311d1d0 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; @@ -1870,34 +1876,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // have become out of sync. removePointersFromTouchTargets(idBitsToAssign); - final float x = ev.getX(actionIndex); - final float y = ev.getY(actionIndex); - - if (mOverlay != null) { - ViewOverlay overlay = (ViewOverlay) mOverlay; - // Check to see whether the overlay can handle the event - final View child = mOverlay; - if (canViewReceivePointerEvents(child) && - isTransformedTouchPointInView(x, y, child, null)) { - newTouchTarget = getTouchTarget(child); - if (newTouchTarget != null) { - newTouchTarget.pointerIdBits |= idBitsToAssign; - } else { - resetCancelNextUpFlag(child); - if (dispatchTransformedTouchEvent(ev, false, child, - idBitsToAssign)) { - mLastTouchDownTime = ev.getDownTime(); - mLastTouchDownX = ev.getX(); - mLastTouchDownY = ev.getY(); - newTouchTarget = addTouchTarget(child, idBitsToAssign); - alreadyDispatchedToNewTouchTarget = true; - } - } - } - } - final int childrenCount = mChildrenCount; if (newTouchTarget == null && childrenCount != 0) { + final float x = ev.getX(actionIndex); + final float y = ev.getY(actionIndex); // Find a child that can receive the event. // Scan children from front to back. final View[] children = mChildren; @@ -2564,7 +2546,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; @@ -2985,6 +2967,30 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** + * Returns the ViewGroupOverlay for this view group, creating it if it does + * not yet exist. In addition to {@link ViewOverlay}'s support for drawables, + * {@link ViewGroupOverlay} allows views to be added to the overlay. These + * views, like overlay drawables, are visual-only; they do not receive input + * events and should not be used as anything other than a temporary + * representation of a view in a parent container, such as might be used + * by an animation effect. + * + * <p>Note: Overlays do not currently work correctly with {@link + * SurfaceView} or {@link TextureView}; contents in overlays for these + * types of views may not display correctly.</p> + * + * @return The ViewGroupOverlay object for this view. + * @see ViewGroupOverlay + */ + @Override + public ViewGroupOverlay getOverlay() { + if (mOverlay == null) { + mOverlay = new ViewGroupOverlay(mContext, this); + } + return (ViewGroupOverlay) mOverlay; + } + + /** * Returns the index of the child to draw for this iteration. Override this * if you want to change the drawing order of children. By default, it * returns i. @@ -3049,11 +3055,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } if (mOverlay != null) { - mOverlay.mRecreateDisplayList = (mOverlay.mPrivateFlags & PFLAG_INVALIDATED) + View overlayView = mOverlay.getOverlayView(); + overlayView.mRecreateDisplayList = (overlayView.mPrivateFlags & PFLAG_INVALIDATED) == PFLAG_INVALIDATED; - mOverlay.mPrivateFlags &= ~PFLAG_INVALIDATED; - mOverlay.getDisplayList(); - mOverlay.mRecreateDisplayList = false; + overlayView.mPrivateFlags &= ~PFLAG_INVALIDATED; + overlayView.getDisplayList(); + overlayView.mRecreateDisplayList = false; } } @@ -4525,7 +4532,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 +5208,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 +5219,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/Overlay.java b/core/java/android/view/ViewGroupOverlay.java index 6630752..c1b24f2 100644 --- a/core/java/android/view/Overlay.java +++ b/core/java/android/view/ViewGroupOverlay.java @@ -15,43 +15,37 @@ */ package android.view; +import android.content.Context; import android.graphics.drawable.Drawable; /** - * An overlay is an extra layer that sits on top of a View (the "host view") - * which is drawn after all other content in that view (including children, - * if the view is a ViewGroup). Interaction with the overlay layer is done in - * terms of adding/removing views and drawables. + * A group overlay is an extra layer that sits on top of a ViewGroup + * (the "host view") which is drawn after all other content in that view + * (including the view group's children). Interaction with the overlay + * layer is done by adding and removing views and drawables. * - * @see android.view.View#getOverlay() + * <p>ViewGroupOverlay is a subclass of {@link ViewOverlay}, adding the ability to + * manage views for overlays on ViewGroups, in addition to the drawable + * support in ViewOverlay.</p> + * + * @see ViewGroup#getOverlay() */ -public interface Overlay { +public class ViewGroupOverlay extends ViewOverlay { - /** - * Adds a Drawable to the overlay. The bounds of the drawable should be relative to - * the host view. Any drawable added to the overlay should be removed when it is no longer - * needed or no longer visible. - * - * @param drawable The Drawable to be added to the overlay. This drawable will be - * drawn when the view redraws its overlay. - * @see #remove(android.graphics.drawable.Drawable) - * @see #add(View) - */ - void add(Drawable drawable); - - /** - * Removes the specified Drawable from the overlay. - * - * @param drawable The Drawable to be removed from the overlay. - * @see #add(android.graphics.drawable.Drawable) - */ - void remove(Drawable drawable); + ViewGroupOverlay(Context context, View hostView) { + super(context, hostView); + } /** * Adds a View to the overlay. The bounds of the added view should be * relative to the host view. Any view added to the overlay should be * removed when it is no longer needed or no longer visible. * + * <p>Views in the overlay are visual-only; they do not receive input + * events and do not participate in focus traversal. Overlay views + * are intended to be transient, such as might be needed by a temporary + * animation effect.</p> + * * <p>If the view has a parent, the view will be removed from that parent * before being added to the overlay. Also, the view will be repositioned * such that it is in the same relative location inside the activity. For @@ -62,20 +56,20 @@ public interface Overlay { * @param view The View to be added to the overlay. The added view will be * drawn when the overlay is drawn. * @see #remove(View) - * @see #add(android.graphics.drawable.Drawable) + * @see ViewOverlay#add(Drawable) */ - void add(View view); + public void add(View view) { + mOverlayViewGroup.add(view); + } /** * Removes the specified View from the overlay. * * @param view The View to be removed from the overlay. * @see #add(View) + * @see ViewOverlay#remove(Drawable) */ - void remove(View view); - - /** - * Removes all views and drawables from the overlay. - */ - void clear(); + public void remove(View view) { + mOverlayViewGroup.remove(view); + } } diff --git a/core/java/android/view/ViewOverlay.java b/core/java/android/view/ViewOverlay.java index 8b18d53..78e2597 100644 --- a/core/java/android/view/ViewOverlay.java +++ b/core/java/android/view/ViewOverlay.java @@ -23,215 +23,286 @@ import android.graphics.drawable.Drawable; import java.util.ArrayList; /** - * ViewOverlay is a container that View uses to host all objects (views and - * drawables) that are added to its "overlay", gotten through - * {@link View#getOverlay()}. Views and drawables are added to the overlay - * via the add/remove methods in this class. These views and drawables are - * drawn whenever the view itself is drawn; first the view draws its own - * content (and children, if it is a ViewGroup), then it draws its overlay - * (if it has one). + * An overlay is an extra layer that sits on top of a View (the "host view") + * which is drawn after all other content in that view (including children, + * if the view is a ViewGroup). Interaction with the overlay layer is done + * by adding and removing drawables. * - * Besides managing and drawing the list of drawables, this class serves - * two purposes: - * (1) it noops layout calls because children are absolutely positioned and - * (2) it forwards all invalidation calls to its host view. The invalidation - * redirect is necessary because the overlay is not a child of the host view - * and invalidation cannot therefore follow the normal path up through the - * parent hierarchy. + * <p>An overlay requested from a ViewGroup is of type {@link ViewGroupOverlay}, + * which also supports adding and removing views.</p> * - * @hide + * @see View#getOverlay() View.getOverlay() + * @see ViewGroup#getOverlay() ViewGroup.getOverlay() + * @see ViewGroupOverlay */ -class ViewOverlay extends ViewGroup implements Overlay { +public class ViewOverlay { /** - * The View for which this is an overlay. Invalidations of the overlay are redirected to - * this host view. + * The actual container for the drawables (and views, if it's a ViewGroupOverlay). + * All of the management and rendering details for the overlay are handled in + * OverlayViewGroup. */ - View mHostView; + OverlayViewGroup mOverlayViewGroup; + + ViewOverlay(Context context, View hostView) { + mOverlayViewGroup = new OverlayViewGroup(context, hostView); + } /** - * The set of drawables to draw when the overlay is rendered. + * Used internally by View and ViewGroup to handle drawing and invalidation + * of the overlay + * @return */ - ArrayList<Drawable> mDrawables = null; - - ViewOverlay(Context context, View host) { - super(context); - mHostView = host; - mParent = mHostView.getParent(); + ViewGroup getOverlayView() { + return mOverlayViewGroup; } - @Override + /** + * Adds a Drawable to the overlay. The bounds of the drawable should be relative to + * the host view. Any drawable added to the overlay should be removed when it is no longer + * needed or no longer visible. + * + * @param drawable The Drawable to be added to the overlay. This drawable will be + * drawn when the view redraws its overlay. + * @see #remove(Drawable) + */ public void add(Drawable drawable) { - if (mDrawables == null) { - mDrawables = new ArrayList<Drawable>(); - } - if (!mDrawables.contains(drawable)) { - // Make each drawable unique in the overlay; can't add it more than once - mDrawables.add(drawable); - invalidate(drawable.getBounds()); - drawable.setCallback(this); - } + mOverlayViewGroup.add(drawable); } - @Override + /** + * Removes the specified Drawable from the overlay. + * + * @param drawable The Drawable to be removed from the overlay. + * @see #add(Drawable) + */ public void remove(Drawable drawable) { - if (mDrawables != null) { - mDrawables.remove(drawable); - invalidate(drawable.getBounds()); - drawable.setCallback(null); - } + mOverlayViewGroup.remove(drawable); } - @Override - public void invalidateDrawable(Drawable drawable) { - invalidate(drawable.getBounds()); + /** + * Removes all content from the overlay. + */ + public void clear() { + mOverlayViewGroup.clear(); } - @Override - public void add(View child) { - int deltaX = 0; - int deltaY = 0; - if (child.getParent() instanceof ViewGroup) { - ViewGroup parent = (ViewGroup) child.getParent(); - if (parent != mHostView) { - // Moving to different container; figure out how to position child such that - // it is in the same location on the screen - int[] parentLocation = new int[2]; - int[] hostViewLocation = new int[2]; - parent.getLocationOnScreen(parentLocation); - mHostView.getLocationOnScreen(hostViewLocation); - child.offsetLeftAndRight(parentLocation[0] - hostViewLocation[0]); - child.offsetTopAndBottom(parentLocation[1] - hostViewLocation[1]); + boolean isEmpty() { + return mOverlayViewGroup.isEmpty(); + } + + /** + * OverlayViewGroup is a container that View and ViewGroup use to host + * drawables and views added to their overlays ({@link ViewOverlay} and + * {@link ViewGroupOverlay}, respectively). Drawables are added to the overlay + * via the add/remove methods in ViewOverlay, Views are added/removed via + * ViewGroupOverlay. These drawable and view objects are + * drawn whenever the view itself is drawn; first the view draws its own + * content (and children, if it is a ViewGroup), then it draws its overlay + * (if it has one). + * + * <p>Besides managing and drawing the list of drawables, this class serves + * two purposes: + * (1) it noops layout calls because children are absolutely positioned and + * (2) it forwards all invalidation calls to its host view. The invalidation + * redirect is necessary because the overlay is not a child of the host view + * and invalidation cannot therefore follow the normal path up through the + * parent hierarchy.</p> + * + * @see View#getOverlay() + * @see ViewGroup#getOverlay() + */ + static class OverlayViewGroup extends ViewGroup { + + /** + * The View for which this is an overlay. Invalidations of the overlay are redirected to + * this host view. + */ + View mHostView; + + /** + * The set of drawables to draw when the overlay is rendered. + */ + ArrayList<Drawable> mDrawables = null; + + OverlayViewGroup(Context context, View hostView) { + super(context); + mHostView = hostView; + mAttachInfo = mHostView.mAttachInfo; + mRight = hostView.getWidth(); + mBottom = hostView.getHeight(); + } + + public void add(Drawable drawable) { + if (mDrawables == null) { + + mDrawables = new ArrayList<Drawable>(); + } + if (!mDrawables.contains(drawable)) { + // Make each drawable unique in the overlay; can't add it more than once + mDrawables.add(drawable); + invalidate(drawable.getBounds()); + drawable.setCallback(this); } - parent.removeView(child); } - super.addView(child); - } - @Override - public void remove(View view) { - super.removeView(view); - } + public void remove(Drawable drawable) { + if (mDrawables != null) { + mDrawables.remove(drawable); + invalidate(drawable.getBounds()); + drawable.setCallback(null); + } + } - @Override - public void clear() { - removeAllViews(); - mDrawables.clear(); - } + public void add(View child) { + if (child.getParent() instanceof ViewGroup) { + ViewGroup parent = (ViewGroup) child.getParent(); + if (parent != mHostView) { + // Moving to different container; figure out how to position child such that + // it is in the same location on the screen + int[] parentLocation = new int[2]; + int[] hostViewLocation = new int[2]; + parent.getLocationOnScreen(parentLocation); + mHostView.getLocationOnScreen(hostViewLocation); + child.offsetLeftAndRight(parentLocation[0] - hostViewLocation[0]); + child.offsetTopAndBottom(parentLocation[1] - hostViewLocation[1]); + } + parent.removeView(child); + } + super.addView(child); + } - boolean isEmpty() { - if (getChildCount() == 0 && (mDrawables == null || mDrawables.size() == 0)) { - return true; + public void remove(View view) { + super.removeView(view); } - return false; - } - @Override - protected void dispatchDraw(Canvas canvas) { - super.dispatchDraw(canvas); - final int numDrawables = (mDrawables == null) ? 0 : mDrawables.size(); - for (int i = 0; i < numDrawables; ++i) { - mDrawables.get(i).draw(canvas); + public void clear() { + removeAllViews(); + mDrawables.clear(); } - } - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - // Noop: children are positioned absolutely - } + boolean isEmpty() { + if (getChildCount() == 0 && + (mDrawables == null || mDrawables.size() == 0)) { + return true; + } + return false; + } - /* - The following invalidation overrides exist for the purpose of redirecting invalidation to - the host view. The overlay is not parented to the host view (since a View cannot be a parent), - so the invalidation cannot proceed through the normal parent hierarchy. - There is a built-in assumption that the overlay exactly covers the host view, therefore - the invalidation rectangles received do not need to be adjusted when forwarded to - the host view. - */ + @Override + public void invalidateDrawable(Drawable drawable) { + invalidate(drawable.getBounds()); + } - @Override - public void invalidate(Rect dirty) { - super.invalidate(dirty); - if (mHostView != null) { - mHostView.invalidate(dirty); + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + final int numDrawables = (mDrawables == null) ? 0 : mDrawables.size(); + for (int i = 0; i < numDrawables; ++i) { + mDrawables.get(i).draw(canvas); + } } - } - @Override - public void invalidate(int l, int t, int r, int b) { - super.invalidate(l, t, r, b); - if (mHostView != null) { - mHostView.invalidate(l, t, r, b); + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + // Noop: children are positioned absolutely } - } - @Override - public void invalidate() { - super.invalidate(); - if (mHostView != null) { - mHostView.invalidate(); + /* + The following invalidation overrides exist for the purpose of redirecting invalidation to + the host view. The overlay is not parented to the host view (since a View cannot be a + parent), so the invalidation cannot proceed through the normal parent hierarchy. + There is a built-in assumption that the overlay exactly covers the host view, therefore + the invalidation rectangles received do not need to be adjusted when forwarded to + the host view. + */ + + @Override + public void invalidate(Rect dirty) { + super.invalidate(dirty); + if (mHostView != null) { + mHostView.invalidate(dirty); + } } - } - @Override - void invalidate(boolean invalidateCache) { - super.invalidate(invalidateCache); - if (mHostView != null) { - mHostView.invalidate(invalidateCache); + @Override + public void invalidate(int l, int t, int r, int b) { + super.invalidate(l, t, r, b); + if (mHostView != null) { + mHostView.invalidate(l, t, r, b); + } } - } - @Override - void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) { - super.invalidateViewProperty(invalidateParent, forceRedraw); - if (mHostView != null) { - mHostView.invalidateViewProperty(invalidateParent, forceRedraw); + @Override + public void invalidate() { + super.invalidate(); + if (mHostView != null) { + mHostView.invalidate(); + } } - } - @Override - protected void invalidateParentCaches() { - super.invalidateParentCaches(); - if (mHostView != null) { - mHostView.invalidateParentCaches(); + @Override + void invalidate(boolean invalidateCache) { + super.invalidate(invalidateCache); + if (mHostView != null) { + mHostView.invalidate(invalidateCache); + } } - } - @Override - protected void invalidateParentIfNeeded() { - super.invalidateParentIfNeeded(); - if (mHostView != null) { - mHostView.invalidateParentIfNeeded(); + @Override + void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) { + super.invalidateViewProperty(invalidateParent, forceRedraw); + if (mHostView != null) { + mHostView.invalidateViewProperty(invalidateParent, forceRedraw); + } } - } - public void invalidateChildFast(View child, final Rect dirty) { - if (mHostView != null) { - // Note: This is not a "fast" invalidation. Would be nice to instead invalidate using DL - // properties and a dirty rect instead of causing a real invalidation of the host view - int left = child.mLeft; - int top = child.mTop; - if (!child.getMatrix().isIdentity()) { - child.transformRect(dirty); + @Override + protected void invalidateParentCaches() { + super.invalidateParentCaches(); + if (mHostView != null) { + mHostView.invalidateParentCaches(); + } + } + + @Override + protected void invalidateParentIfNeeded() { + super.invalidateParentIfNeeded(); + if (mHostView != null) { + mHostView.invalidateParentIfNeeded(); } - dirty.offset(left, top); - mHostView.invalidate(dirty); } - } - @Override - public ViewParent invalidateChildInParent(int[] location, Rect dirty) { - if (mHostView != null) { - dirty.offset(location[0], location[1]); - if (mHostView instanceof ViewGroup) { - location[0] = 0; - location[1] = 0; - super.invalidateChildInParent(location, dirty); - return ((ViewGroup) mHostView).invalidateChildInParent(location, dirty); - } else { - invalidate(dirty); + public void invalidateChildFast(View child, final Rect dirty) { + if (mHostView != null) { + // Note: This is not a "fast" invalidation. Would be nice to instead invalidate + // using DisplayList properties and a dirty rect instead of causing a real + // invalidation of the host view + int left = child.mLeft; + int top = child.mTop; + if (!child.getMatrix().isIdentity()) { + child.transformRect(dirty); + } + dirty.offset(left, top); + mHostView.invalidate(dirty); } } - return null; + + @Override + public ViewParent invalidateChildInParent(int[] location, Rect dirty) { + if (mHostView != null) { + dirty.offset(location[0], location[1]); + if (mHostView instanceof ViewGroup) { + location[0] = 0; + location[1] = 0; + super.invalidateChildInParent(location, dirty); + return ((ViewGroup) mHostView).invalidateChildInParent(location, dirty); + } else { + invalidate(dirty); + } + } + return null; + } } + } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7790f92..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 @@ -1425,6 +1425,8 @@ public final class ViewRootImpl implements ViewParent, final int surfaceGenerationId = mSurface.getGenerationId(); relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); + mWindowsAnimating |= + (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_ANIMATING) != 0; if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString() + " overscan=" + mPendingOverscanInsets.toShortString() @@ -2385,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 @@ -2447,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; @@ -3097,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 4207832..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,17 +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; } - + + setInputChannelLocked(res.channel); mCurMethod = res.method; - if (mCurChannel != null) { - mCurChannel.dispose(); - } - mCurChannel = res.channel; mCurId = res.id; mBindSequence = res.sequence; } @@ -718,19 +716,26 @@ public final class InputMethodManager { */ void clearBindingLocked() { clearConnectionLocked(); + 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 @@ -1172,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) { @@ -1653,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); @@ -1662,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 { @@ -1678,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; @@ -1712,6 +1725,18 @@ public final class InputMethodManager { } } + private void flushPendingEventsLocked() { + mH.removeMessages(MSG_EVENT_TIMEOUT); + + PendingEvent p = mPendingEventHead; + while (p != null) { + Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, p.mSeq, 0, p); + msg.setAsynchronous(true); + mH.sendMessage(msg); + p = p.mNext; + } + } + public void showInputMethodPicker() { synchronized (mH) { showInputMethodPickerLocked(); diff --git a/core/java/android/webkit/HTML5Audio.java b/core/java/android/webkit/HTML5Audio.java index 684ec07..17eb2df 100644 --- a/core/java/android/webkit/HTML5Audio.java +++ b/core/java/android/webkit/HTML5Audio.java @@ -19,6 +19,7 @@ package android.webkit; import android.content.Context; import android.media.AudioManager; import android.media.MediaPlayer; +import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -84,6 +85,7 @@ class HTML5Audio extends Handler // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate private Timer mTimer; private final class TimeupdateTask extends TimerTask { + @Override public void run() { HTML5Audio.this.obtainMessage(TIMEUPDATE).sendToTarget(); } @@ -139,11 +141,13 @@ class HTML5Audio extends Handler // (i.e. the webviewcore thread here) // MediaPlayer.OnBufferingUpdateListener + @Override public void onBufferingUpdate(MediaPlayer mp, int percent) { nativeOnBuffering(percent, mNativePointer); } // MediaPlayer.OnCompletionListener; + @Override public void onCompletion(MediaPlayer mp) { mState = COMPLETE; mProcessingOnEnd = true; @@ -156,6 +160,7 @@ class HTML5Audio extends Handler } // MediaPlayer.OnErrorListener + @Override public boolean onError(MediaPlayer mp, int what, int extra) { mState = ERROR; resetMediaPlayer(); @@ -164,6 +169,7 @@ class HTML5Audio extends Handler } // MediaPlayer.OnPreparedListener + @Override public void onPrepared(MediaPlayer mp) { mState = PREPARED; if (mTimer != null) { @@ -178,6 +184,7 @@ class HTML5Audio extends Handler } // MediaPlayer.OnSeekCompleteListener + @Override public void onSeekComplete(MediaPlayer mp) { nativeOnTimeupdate(mp.getCurrentPosition(), mNativePointer); } @@ -231,7 +238,7 @@ class HTML5Audio extends Handler headers.put(HIDE_URL_LOGS, "true"); } - mMediaPlayer.setDataSource(url, headers); + mMediaPlayer.setDataSource(mContext, Uri.parse(url), headers); mState = INITIALIZED; mMediaPlayer.prepareAsync(); } catch (IOException e) { diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 18df0b1..00d87bd 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -25,8 +25,13 @@ import dalvik.system.PathClassLoader; /** * Top level factory, used creating all the main WebView implementation classes. + * + * @hide */ -class WebViewFactory { +public final class WebViewFactory { + public static final String WEBVIEW_EXPERIMENTAL_PROPERTY = "persist.sys.webview.exp"; + private static final String DEPRECATED_CHROMIUM_PROPERTY = "webview.use_chromium"; + // Default Provider factory class name. // TODO: When the Chromium powered WebView is ready, it should be the default factory class. private static final String DEFAULT_WEBVIEW_FACTORY = "android.webkit.WebViewClassic$Factory"; @@ -43,16 +48,17 @@ class WebViewFactory { private static WebViewFactoryProvider sProviderInstance; private static final Object sProviderLock = new Object(); + public static boolean isExperimentalWebViewAvailable() { + return Build.IS_DEBUGGABLE && (new java.io.File(CHROMIUM_WEBVIEW_JAR).exists()); + } + static WebViewFactoryProvider getProvider() { synchronized (sProviderLock) { // For now the main purpose of this function (and the factory abstraction) is to keep // us honest and minimize usage of WebViewClassic internals when binding the proxy. if (sProviderInstance != null) return sProviderInstance; - // For debug builds, we allow a system property to specify that we should use the - // Chromium powered WebView. This enables us to switch between implementations - // at runtime. For user (release) builds, don't allow this. - if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("webview.use_chromium", false)) { + if (isExperimentalWebViewEnabled()) { StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); try { sProviderInstance = loadChromiumProvider(); @@ -76,6 +82,20 @@ class WebViewFactory { } } + // For debug builds, we allow a system property to specify that we should use the + // experimtanl Chromium powered WebView. This enables us to switch between + // implementations at runtime. For user (release) builds, don't allow this. + private static boolean isExperimentalWebViewEnabled() { + if (!isExperimentalWebViewAvailable()) + return false; + if (SystemProperties.getBoolean(DEPRECATED_CHROMIUM_PROPERTY, false)) { + Log.w(LOGTAG, String.format("The property %s has been deprecated. Please use %s.", + DEPRECATED_CHROMIUM_PROPERTY, WEBVIEW_EXPERIMENTAL_PROPERTY)); + return true; + } + return SystemProperties.getBoolean(WEBVIEW_EXPERIMENTAL_PROPERTY, false); + } + // TODO: This allows us to have the legacy and Chromium WebView coexist for development // and side-by-side testing. After transition, remove this when no longer required. private static WebViewFactoryProvider loadChromiumProvider() { diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java index b2073b1..34cfea5 100644 --- a/core/java/android/widget/AppSecurityPermissions.java +++ b/core/java/android/widget/AppSecurityPermissions.java @@ -20,6 +20,7 @@ import com.android.internal.R; import android.app.AlertDialog; import android.content.Context; +import android.content.DialogInterface; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -77,6 +78,7 @@ public class AppSecurityPermissions { private final PermissionInfoComparator mPermComparator = new PermissionInfoComparator(); private final List<MyPermissionInfo> mPermsList = new ArrayList<MyPermissionInfo>(); private final CharSequence mNewPermPrefix; + private String mPackageName; static class MyPermissionGroupInfo extends PermissionGroupInfo { CharSequence mLabel; @@ -138,6 +140,8 @@ public class AppSecurityPermissions { MyPermissionGroupInfo mGroup; MyPermissionInfo mPerm; AlertDialog mDialog; + private boolean mShowRevokeUI = false; + private String mPackageName; public PermissionItemView(Context context, AttributeSet attrs) { super(context, attrs); @@ -145,9 +149,12 @@ public class AppSecurityPermissions { } public void setPermission(MyPermissionGroupInfo grp, MyPermissionInfo perm, - boolean first, CharSequence newPermPrefix) { + boolean first, CharSequence newPermPrefix, String packageName, + boolean showRevokeUI) { mGroup = grp; mPerm = perm; + mShowRevokeUI = showRevokeUI; + mPackageName = packageName; ImageView permGrpIcon = (ImageView) findViewById(R.id.perm_icon); TextView permNameView = (TextView) findViewById(R.id.perm_name); @@ -206,6 +213,7 @@ public class AppSecurityPermissions { } builder.setCancelable(true); builder.setIcon(mGroup.loadGroupIcon(pm)); + addRevokeUIIfNecessary(builder); mDialog = builder.show(); mDialog.setCanceledOnTouchOutside(true); } @@ -218,6 +226,30 @@ public class AppSecurityPermissions { mDialog.dismiss(); } } + + private void addRevokeUIIfNecessary(AlertDialog.Builder builder) { + if (!mShowRevokeUI) { + return; + } + + final boolean isRequired = + ((mPerm.mExistingReqFlags & PackageInfo.REQUESTED_PERMISSION_REQUIRED) != 0); + + if (isRequired) { + return; + } + + DialogInterface.OnClickListener ocl = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + PackageManager pm = getContext().getPackageManager(); + pm.revokePermission(mPackageName, mPerm.name); + PermissionItemView.this.setVisibility(View.GONE); + } + }; + builder.setNegativeButton(R.string.revoke, ocl); + builder.setPositiveButton(R.string.ok, null); + } } private AppSecurityPermissions(Context context) { @@ -230,6 +262,7 @@ public class AppSecurityPermissions { public AppSecurityPermissions(Context context, String packageName) { this(context); + mPackageName = packageName; Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>(); PackageInfo pkgInfo; try { @@ -252,6 +285,7 @@ public class AppSecurityPermissions { if(info == null) { return; } + mPackageName = info.packageName; // Convert to a PackageInfo PackageInfo installedPkgInfo = null; @@ -419,15 +453,23 @@ public class AppSecurityPermissions { } public View getPermissionsView() { - return getPermissionsView(WHICH_ALL); + return getPermissionsView(WHICH_ALL, false); + } + + public View getPermissionsViewWithRevokeButtons() { + return getPermissionsView(WHICH_ALL, true); } public View getPermissionsView(int which) { + return getPermissionsView(which, false); + } + + private View getPermissionsView(int which, boolean showRevokeUI) { LinearLayout permsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null); LinearLayout displayList = (LinearLayout) permsView.findViewById(R.id.perms_list); View noPermsView = permsView.findViewById(R.id.no_permissions); - displayPermissions(mPermGroupsList, displayList, which); + displayPermissions(mPermGroupsList, displayList, which, showRevokeUI); if (displayList.getChildCount() <= 0) { noPermsView.setVisibility(View.VISIBLE); } @@ -440,7 +482,7 @@ public class AppSecurityPermissions { * list of permission descriptions. */ private void displayPermissions(List<MyPermissionGroupInfo> groups, - LinearLayout permListView, int which) { + LinearLayout permListView, int which, boolean showRevokeUI) { permListView.removeAllViews(); int spacing = (int)(8*mContext.getResources().getDisplayMetrics().density); @@ -451,7 +493,7 @@ public class AppSecurityPermissions { for (int j=0; j<perms.size(); j++) { MyPermissionInfo perm = perms.get(j); View view = getPermissionItemView(grp, perm, j == 0, - which != WHICH_NEW ? mNewPermPrefix : null); + which != WHICH_NEW ? mNewPermPrefix : null, showRevokeUI); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); @@ -470,18 +512,19 @@ public class AppSecurityPermissions { } private PermissionItemView getPermissionItemView(MyPermissionGroupInfo grp, - MyPermissionInfo perm, boolean first, CharSequence newPermPrefix) { - return getPermissionItemView(mContext, mInflater, grp, perm, first, newPermPrefix); + MyPermissionInfo perm, boolean first, CharSequence newPermPrefix, boolean showRevokeUI) { + return getPermissionItemView(mContext, mInflater, grp, perm, first, newPermPrefix, + mPackageName, showRevokeUI); } private static PermissionItemView getPermissionItemView(Context context, LayoutInflater inflater, MyPermissionGroupInfo grp, MyPermissionInfo perm, boolean first, - CharSequence newPermPrefix) { - PermissionItemView permView = (PermissionItemView)inflater.inflate( + CharSequence newPermPrefix, String packageName, boolean showRevokeUI) { + PermissionItemView permView = (PermissionItemView)inflater.inflate( (perm.flags & PermissionInfo.FLAG_COSTS_MONEY) != 0 ? R.layout.app_permission_item_money : R.layout.app_permission_item, null); - permView.setPermission(grp, perm, first, newPermPrefix); + permView.setPermission(grp, perm, first, newPermPrefix, packageName, showRevokeUI); return permView; } diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 4b62c2d..c7914f3 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -2433,7 +2433,7 @@ public class ListView extends AbsListView { mFirstPosition; } else { final int lastPos = mFirstPosition + getChildCount() - 1; - nextSelected = selectedPos != INVALID_POSITION && selectedPos < lastPos? + nextSelected = selectedPos != INVALID_POSITION && selectedPos <= lastPos ? selectedPos - 1 : lastPos; } diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index 529de2e..3df7258 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -220,28 +220,29 @@ public class RelativeLayout extends ViewGroup { // with MeasureSpec value overflow and RelativeLayout was one source of them. // Some apps came to rely on them. :( private boolean mAllowBrokenMeasureSpecs = false; + // Compatibility hack. Old versions of the platform would not take + // margins and padding into account when generating the height measure spec + // for children during the horizontal measure pass. + private boolean mMeasureVerticalWithPaddingMargin = false; // A default width used for RTL measure pass - private static int DEFAULT_WIDTH = Integer.MAX_VALUE / 2; + private static final int DEFAULT_WIDTH = Integer.MAX_VALUE / 2; public RelativeLayout(Context context) { super(context); - mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <= - Build.VERSION_CODES.JELLY_BEAN_MR1; + queryCompatibilityModes(context); } public RelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); initFromAttributes(context, attrs); - mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <= - Build.VERSION_CODES.JELLY_BEAN_MR1; + queryCompatibilityModes(context); } public RelativeLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initFromAttributes(context, attrs); - mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <= - Build.VERSION_CODES.JELLY_BEAN_MR1; + queryCompatibilityModes(context); } private void initFromAttributes(Context context, AttributeSet attrs) { @@ -251,6 +252,12 @@ public class RelativeLayout extends ViewGroup { a.recycle(); } + private void queryCompatibilityModes(Context context) { + int version = context.getApplicationInfo().targetSdkVersion; + mAllowBrokenMeasureSpecs = version <= Build.VERSION_CODES.JELLY_BEAN_MR1; + mMeasureVerticalWithPaddingMargin = version >= Build.VERSION_CODES.JELLY_BEAN_MR2; + } + @Override public boolean shouldDelayChildPressedState() { return false; @@ -692,6 +699,11 @@ public class RelativeLayout extends ViewGroup { params.leftMargin, params.rightMargin, mPaddingLeft, mPaddingRight, myWidth); + int maxHeight = myHeight; + if (mMeasureVerticalWithPaddingMargin) { + maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom - + params.topMargin - params.bottomMargin); + } int childHeightMeasureSpec; if (myHeight < 0 && !mAllowBrokenMeasureSpecs) { if (params.height >= 0) { @@ -704,9 +716,9 @@ public class RelativeLayout extends ViewGroup { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } } else if (params.width == LayoutParams.MATCH_PARENT) { - childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY); + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY); } else { - childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST); + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST); } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } 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. * diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index db20549..acbb2b1 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -812,6 +812,26 @@ public class ActionBarImpl extends ActionBar { return mActionView != null && mActionView.isTitleTruncated(); } + @Override + public void setHomeAsUpIndicator(Drawable indicator) { + mActionView.setHomeAsUpIndicator(indicator); + } + + @Override + public void setHomeAsUpIndicator(int resId) { + mActionView.setHomeAsUpIndicator(resId); + } + + @Override + public void setHomeActionContentDescription(CharSequence description) { + mActionView.setHomeActionContentDescription(description); + } + + @Override + public void setHomeActionContentDescription(int resId) { + mActionView.setHomeActionContentDescription(resId); + } + /** * @hide */ diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java new file mode 100644 index 0000000..e26b27d --- /dev/null +++ b/core/java/com/android/internal/os/BaseCommand.java @@ -0,0 +1,146 @@ +/* +** +** Copyright 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 com.android.internal.os; + +import java.io.PrintStream; + +public abstract class BaseCommand { + + protected String[] mArgs; + private int mNextArg; + private String mCurArgData; + + // These are magic strings understood by the Eclipse plugin. + public static final String FATAL_ERROR_CODE = "Error type 1"; + public static final String NO_SYSTEM_ERROR_CODE = "Error type 2"; + public static final String NO_CLASS_ERROR_CODE = "Error type 3"; + + /** + * Call to run the command. + */ + public void run(String[] args) { + if (args.length < 1) { + onShowUsage(System.out); + return; + } + + mArgs = args; + mNextArg = 0; + mCurArgData = null; + + try { + onRun(); + } catch (IllegalArgumentException e) { + onShowUsage(System.err); + System.err.println(); + System.err.println("Error: " + e.getMessage()); + } catch (Exception e) { + e.printStackTrace(System.err); + System.exit(1); + } + } + + /** + * Convenience to show usage information to error output. + */ + public void showUsage() { + onShowUsage(System.err); + } + + /** + * Convenience to show usage information to error output along + * with an error message. + */ + public void showError(String message) { + onShowUsage(System.err); + System.err.println(); + System.err.println(message); + } + + /** + * Implement the command. + */ + public abstract void onRun() throws Exception; + + /** + * Print help text for the command. + */ + public abstract void onShowUsage(PrintStream out); + + /** + * Return the next option on the command line -- that is an argument that + * starts with '-'. If the next argument is not an option, null is returned. + */ + public String nextOption() { + if (mCurArgData != null) { + String prev = mArgs[mNextArg - 1]; + throw new IllegalArgumentException("No argument expected after \"" + prev + "\""); + } + if (mNextArg >= mArgs.length) { + return null; + } + String arg = mArgs[mNextArg]; + if (!arg.startsWith("-")) { + return null; + } + mNextArg++; + if (arg.equals("--")) { + return null; + } + if (arg.length() > 1 && arg.charAt(1) != '-') { + if (arg.length() > 2) { + mCurArgData = arg.substring(2); + return arg.substring(0, 2); + } else { + mCurArgData = null; + return arg; + } + } + mCurArgData = null; + return arg; + } + + /** + * Return the next argument on the command line, whatever it is; if there are + * no arguments left, return null. + */ + public String nextArg() { + if (mCurArgData != null) { + String arg = mCurArgData; + mCurArgData = null; + return arg; + } else if (mNextArg < mArgs.length) { + return mArgs[mNextArg++]; + } else { + return null; + } + } + + /** + * Return the next argument on the command line, whatever it is; if there are + * no arguments left, throws an IllegalArgumentException to report this to the user. + */ + public String nextArgRequired() { + String arg = nextArg(); + if (arg == null) { + String prev = mArgs[mNextArg - 1]; + throw new IllegalArgumentException("Argument expected after \"" + prev + "\""); + } + return arg; + } +} diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 780f5b3..58b15e2 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -17,7 +17,7 @@ package com.android.internal.statusbar; import com.android.internal.statusbar.StatusBarIcon; -import com.android.internal.statusbar.StatusBarNotification; +import android.service.notification.StatusBarNotification; /** @hide */ oneway interface IStatusBar diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 04e5bc9..c98ba8d 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -19,7 +19,7 @@ package com.android.internal.statusbar; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; -import com.android.internal.statusbar.StatusBarNotification; +import android.service.notification.StatusBarNotification; /** @hide */ interface IStatusBarService diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java index 9143c61..14afe21 100644 --- a/core/java/com/android/internal/view/InputBindResult.java +++ b/core/java/com/android/internal/view/InputBindResult.java @@ -84,7 +84,7 @@ public final class InputBindResult implements Parcelable { dest.writeStrongInterface(method); if (channel != null) { dest.writeInt(1); - channel.writeToParcel(dest, 0); + channel.writeToParcel(dest, flags); } else { dest.writeInt(0); } diff --git a/core/java/com/android/internal/view/menu/ActionMenuItem.java b/core/java/com/android/internal/view/menu/ActionMenuItem.java index 2685046..7ca6c1b 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuItem.java +++ b/core/java/com/android/internal/view/menu/ActionMenuItem.java @@ -107,7 +107,7 @@ public class ActionMenuItem implements MenuItem { } public CharSequence getTitleCondensed() { - return mTitleCondensed; + return mTitleCondensed != null ? mTitleCondensed : mTitle; } public boolean hasSubMenu() { diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index d1db230..59ff597 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -36,7 +36,6 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.content.res.TypedArray; -import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; @@ -94,6 +93,8 @@ public class ActionBarView extends AbsActionBarView { private CharSequence mSubtitle; private Drawable mIcon; private Drawable mLogo; + private CharSequence mHomeDescription; + private int mHomeDescriptionRes; private HomeView mHomeLayout; private HomeView mExpandedHomeLayout; @@ -125,6 +126,7 @@ public class ActionBarView extends AbsActionBarView { private boolean mWasHomeEnabled; // Was it enabled before action view expansion? private MenuBuilder mOptionsMenu; + private boolean mMenuPrepared; private ActionBarContextView mContextView; @@ -164,7 +166,10 @@ public class ActionBarView extends AbsActionBarView { private final OnClickListener mUpClickListener = new OnClickListener() { public void onClick(View v) { - mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem); + if (mMenuPrepared) { + // Only invoke the window callback if the options menu has been initialized. + mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem); + } } }; @@ -285,6 +290,10 @@ public class ActionBarView extends AbsActionBarView { initTitle(); } + if (mHomeDescriptionRes != 0) { + setHomeActionContentDescription(mHomeDescriptionRes); + } + if (mTabScrollView != null && mIncludeTabs) { ViewGroup.LayoutParams lp = mTabScrollView.getLayoutParams(); if (lp != null) { @@ -402,6 +411,10 @@ public class ActionBarView extends AbsActionBarView { mCallback = callback; } + public void setMenuPrepared() { + mMenuPrepared = true; + } + public void setMenu(Menu menu, MenuPresenter.Callback cb) { if (menu == mOptionsMenu) return; @@ -582,14 +595,43 @@ public class ActionBarView extends AbsActionBarView { mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); } else { mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_AUTO); + mUpGoerFive.setContentDescription(buildHomeContentDescription()); + } + } + + /** + * Compose a content description for the Home/Up affordance. + * + * <p>As this encompasses the icon/logo, title and subtitle all in one, we need + * a description for the whole wad of stuff that can be localized properly.</p> + */ + private CharSequence buildHomeContentDescription() { + final CharSequence homeDesc; + if (mHomeDescription != null) { + homeDesc = mHomeDescription; + } else { if ((mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0) { - mUpGoerFive.setContentDescription(mContext.getResources().getText( - R.string.action_bar_up_description)); + homeDesc = mContext.getResources().getText(R.string.action_bar_up_description); } else { - mUpGoerFive.setContentDescription(mContext.getResources().getText( - R.string.action_bar_home_description)); + homeDesc = mContext.getResources().getText(R.string.action_bar_home_description); + } + } + + final CharSequence title = getTitle(); + final CharSequence subtitle = getSubtitle(); + if (!TextUtils.isEmpty(title)) { + final String result; + if (!TextUtils.isEmpty(subtitle)) { + result = getResources().getString( + R.string.action_bar_home_subtitle_description_format, + title, subtitle, homeDesc); + } else { + result = getResources().getString(R.string.action_bar_home_description_format, + title, homeDesc); } + return result; } + return homeDesc; } public void setDisplayOptions(int options) { @@ -1298,6 +1340,23 @@ public class ActionBarView extends AbsActionBarView { } } + public void setHomeAsUpIndicator(Drawable indicator) { + mHomeLayout.setUpIndicator(indicator); + } + + public void setHomeAsUpIndicator(int resId) { + mHomeLayout.setUpIndicator(resId); + } + + public void setHomeActionContentDescription(CharSequence description) { + mHomeDescription = description; + } + + public void setHomeActionContentDescription(int resId) { + mHomeDescriptionRes = resId; + mHomeDescription = getResources().getText(resId); + } + static class SavedState extends BaseSavedState { int expandedMenuItemId; boolean isOverflowOpen; @@ -1332,9 +1391,11 @@ public class ActionBarView extends AbsActionBarView { } private static class HomeView extends FrameLayout { - private View mUpView; + private ImageView mUpView; private ImageView mIconView; private int mUpWidth; + private int mUpIndicatorRes; + private Drawable mDefaultUpIndicator; private static final long DEFAULT_TRANSITION_DURATION = 150; @@ -1359,6 +1420,25 @@ public class ActionBarView extends AbsActionBarView { mIconView.setImageDrawable(icon); } + public void setUpIndicator(Drawable d) { + mUpView.setImageDrawable(d != null ? d : mDefaultUpIndicator); + mUpIndicatorRes = 0; + } + + public void setUpIndicator(int resId) { + mUpIndicatorRes = resId; + mUpView.setImageDrawable(resId != 0 ? getResources().getDrawable(resId) : null); + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + if (mUpIndicatorRes != 0) { + // Reload for config change + setUpIndicator(mUpIndicatorRes); + } + } + @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { onPopulateAccessibilityEvent(event); @@ -1382,8 +1462,9 @@ public class ActionBarView extends AbsActionBarView { @Override protected void onFinishInflate() { - mUpView = findViewById(com.android.internal.R.id.up); + mUpView = (ImageView) findViewById(com.android.internal.R.id.up); mIconView = (ImageView) findViewById(com.android.internal.R.id.home); + mDefaultUpIndicator = mUpView.getDrawable(); } public int getStartOffset() { diff --git a/core/java/com/android/internal/widget/TransportControlView.java b/core/java/com/android/internal/widget/TransportControlView.java index 8ebe94c..ca797eb 100644 --- a/core/java/com/android/internal/widget/TransportControlView.java +++ b/core/java/com/android/internal/widget/TransportControlView.java @@ -158,7 +158,7 @@ public class TransportControlView extends FrameLayout implements OnClickListener } } - public void setTransportControlFlags(int generationId, int flags) { + public void setTransportControlInfo(int generationId, int flags, int posCapabilities) { Handler handler = mLocalHandler.get(); if (handler != null) { handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, generationId, flags) |
