diff options
Diffstat (limited to 'core/java')
67 files changed, 1195 insertions, 466 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index c80eeb9..2503d17 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5459,7 +5459,7 @@ public class Activity extends ContextThemeWrapper * this method anytime before a return from {@link #onPause()}. If this call is successful * then the activity will remain visible when {@link #onPause()} is called, and can continue to * play media in the background, but it must stop playing and release resources prior to or - * within the call to {@link #onVisibleBehindCancelled()}. If this call returns false, the + * within the call to {@link #onVisibleBehindCanceled()}. If this call returns false, the * activity will not be visible in the background, and must release any media resources * immediately. * @@ -5475,10 +5475,10 @@ public class Activity extends ContextThemeWrapper * @return the resulting visibiity state. If true the activity may remain visible beyond * {@link #onPause()}. If false then the activity may not count on being visible behind * other translucent activities, and must stop any media playback and release resources. - * Returning false may occur in lieu of a call to onVisibleBehindCancelled() so the return + * Returning false may occur in lieu of a call to onVisibleBehindCanceled() so the return * value must be checked. * - * @see #onVisibleBehindCancelled() + * @see #onVisibleBehindCanceled() * @see #onBackgroundVisibleBehindChanged(boolean) */ public boolean requestVisibleBehind(boolean visible) { @@ -5498,7 +5498,7 @@ public class Activity extends ContextThemeWrapper /** * Called when a translucent activity over this activity is becoming opaque or another * activity is being launched. Activities that override this method must call - * <code>super.onVisibleBehindCancelled()</code> or a SuperNotCalledException will be thrown. + * <code>super.onVisibleBehindCanceled()</code> or a SuperNotCalledException will be thrown. * * <p>When this method is called the activity has 500 msec to release any resources it may be * using while visible in the background. @@ -5509,7 +5509,7 @@ public class Activity extends ContextThemeWrapper * @see #requestVisibleBehind(boolean) * @see #onBackgroundVisibleBehindChanged(boolean) */ - public void onVisibleBehindCancelled() { + public void onVisibleBehindCanceled() { mCalled = true; } @@ -5521,7 +5521,7 @@ public class Activity extends ContextThemeWrapper * {@link #requestVisibleBehind(boolean)}, false otherwise. * * @see #requestVisibleBehind(boolean) - * @see #onVisibleBehindCancelled() + * @see #onVisibleBehindCanceled() * @see #onBackgroundVisibleBehindChanged(boolean) * @hide */ @@ -5544,7 +5544,7 @@ public class Activity extends ContextThemeWrapper * @param visible true if a background activity is visible, false otherwise. * * @see #requestVisibleBehind(boolean) - * @see #onVisibleBehindCancelled() + * @see #onVisibleBehindCanceled() * @hide */ @SystemApi diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index ffb9c95..bc54055 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -52,6 +52,7 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.text.TextUtils; import android.util.DisplayMetrics; +import android.util.Size; import android.util.Slog; import java.io.FileDescriptor; @@ -1026,24 +1027,13 @@ public class ActivityManager { } /** - * Return the current design width for {@link AppTask} thumbnails, for use + * Return the current design dimensions for {@link AppTask} thumbnails, for use * with {@link #addAppTask}. */ - public int getAppTaskThumbnailWidth() { + public Size getAppTaskThumbnailSize() { synchronized (this) { ensureAppTaskThumbnailSizeLocked(); - return mAppTaskThumbnailSize.x; - } - } - - /** - * Return the current design height for {@link AppTask} thumbnails, for use - * with {@link #addAppTask}. - */ - public int getAppTaskThumbnailHeight() { - synchronized (this) { - ensureAppTaskThumbnailSizeLocked(); - return mAppTaskThumbnailSize.y; + return new Size(mAppTaskThumbnailSize.x, mAppTaskThumbnailSize.y); } } @@ -1072,9 +1062,9 @@ public class ActivityManager { * set on it. * @param description Optional additional description information. * @param thumbnail Thumbnail to use for the recents entry. Should be the size given by - * {@link #getAppTaskThumbnailWidth()} and {@link #getAppTaskThumbnailHeight()}. If the - * bitmap is not that exact size, it will be recreated in your process, probably in a way - * you don't like, before the recents entry is added. + * {@link #getAppTaskThumbnailSize()}. If the bitmap is not that exact size, it will be + * recreated in your process, probably in a way you don't like, before the recents entry + * is added. * * @return Returns the task id of the newly added app task, or -1 if the add failed. The * most likely cause of failure is that there is no more room for more tasks for your app. diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index d70e5df..38999a8 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2485,11 +2485,11 @@ public final class ActivityThread { final Activity activity = r.activity; if (activity.mVisibleBehind) { activity.mCalled = false; - activity.onVisibleBehindCancelled(); + activity.onVisibleBehindCanceled(); // Tick, tick, tick. The activity has 500 msec to return or it will be destroyed. if (!activity.mCalled) { throw new SuperNotCalledException("Activity " + activity.getLocalClassName() + - " did not call through to super.onVisibleBehindCancelled()"); + " did not call through to super.onVisibleBehindCanceled()"); } activity.mVisibleBehind = false; } diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java index e4f2b88..a09a2e7 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -217,7 +217,10 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { } protected void viewsReady(ArrayMap<String, View> sharedElements) { - setSharedElements(sharedElements); + sharedElements.retainAll(mAllSharedElementNames); + mListener.remapSharedElements(mAllSharedElementNames, sharedElements); + mSharedElementNames.addAll(sharedElements.keySet()); + mSharedElements.addAll(sharedElements.values()); if (getViewsTransition() != null) { getDecor().captureTransitioningViews(mTransitioningViews); mTransitioningViews.removeAll(mSharedElements); @@ -339,32 +342,16 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { protected ArrayMap<String, View> mapSharedElements(ArrayList<String> accepted, ArrayList<View> localViews) { ArrayMap<String, View> sharedElements = new ArrayMap<String, View>(); - if (!mAllSharedElementNames.isEmpty()) { - if (accepted != null) { - for (int i = 0; i < accepted.size(); i++) { - sharedElements.put(accepted.get(i), localViews.get(i)); - } - } else { - getDecor().findNamedViews(sharedElements); + if (accepted != null) { + for (int i = 0; i < accepted.size(); i++) { + sharedElements.put(accepted.get(i), localViews.get(i)); } + } else { + getDecor().findNamedViews(sharedElements); } return sharedElements; } - private void setSharedElements(ArrayMap<String, View> sharedElements) { - sharedElements.retainAll(mAllSharedElementNames); - mListener.remapSharedElements(mAllSharedElementNames, sharedElements); - sharedElements.retainAll(mAllSharedElementNames); - for (int i = 0; i < mAllSharedElementNames.size(); i++) { - String name = mAllSharedElementNames.get(i); - View sharedElement = sharedElements.get(name); - if (sharedElement != null) { - mSharedElementNames.add(name); - mSharedElements.add(sharedElement); - } - } - } - protected void setResultReceiver(ResultReceiver resultReceiver) { mResultReceiver = resultReceiver; } diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java index 1e556d6..f79d32b 100644 --- a/core/java/android/app/DatePickerDialog.java +++ b/core/java/android/app/DatePickerDialog.java @@ -135,6 +135,9 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, mDatePicker.getMonth(), mDatePicker.getDayOfMonth()); } break; + case BUTTON_NEGATIVE: + cancel(); + break; } } diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index 75ecbd9..5a6898d 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -327,7 +327,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { public void run() { if (mAnimations++ < MIN_ANIMATION_FRAMES) { getDecor().postOnAnimation(this); - } else { + } else if (mResultReceiver != null) { mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null); mResultReceiver = null; // all done sending messages. } diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java index 43b9ea8..f31800d 100644 --- a/core/java/android/app/ExitTransitionCoordinator.java +++ b/core/java/android/app/ExitTransitionCoordinator.java @@ -174,6 +174,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { }); setGhostVisibility(View.INVISIBLE); scheduleGhostVisibilityChange(View.INVISIBLE); + mListener.setSharedElementEnd(mSharedElementNames, mSharedElements, sharedElementSnapshots); TransitionManager.beginDelayedTransition(getDecor(), transition); scheduleGhostVisibilityChange(View.VISIBLE); setGhostVisibility(View.VISIBLE); diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 07e9a94..fb28c5d 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -58,9 +58,12 @@ interface INotificationManager void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id); void cancelNotificationsFromListener(in INotificationListener token, in String[] keys); - ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys); + ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys, int trim); void requestHintsFromListener(in INotificationListener token, int hints); int getHintsFromListener(in INotificationListener token); + void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter); + int getInterruptionFilterFromListener(in INotificationListener token); + void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim); ComponentName getEffectsSuppressor(); diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 50e3a10..e055237 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -56,6 +56,13 @@ public class KeyguardManager { public static final String EXTRA_DESCRIPTION = "android.app.extra.DESCRIPTION"; /** + * @removed + */ + public Intent getConfirmDeviceCredentialIntent(CharSequence title, CharSequence description) { + return createConfirmDeviceCredentialIntent(title, description); + } + + /** * Get an intent to prompt the user to confirm credentials (pin, pattern or password) * for the current user of the device. The caller is expected to launch this activity using * {@link android.app.Activity#startActivityForResult(Intent, int)} and check for @@ -63,7 +70,7 @@ public class KeyguardManager { * * @return the intent for launching the activity or null if no password is required. **/ - public Intent getConfirmDeviceCredentialIntent(CharSequence title, CharSequence description) { + public Intent createConfirmDeviceCredentialIntent(CharSequence title, CharSequence description) { if (!isKeyguardSecure()) return null; Intent intent = new Intent(ACTION_CONFIRM_DEVICE_CREDENTIAL); intent.putExtra(EXTRA_TITLE, title); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index e1dc8bf..966d2ce 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1424,6 +1424,8 @@ public class Notification implements Parcelable extras.remove(Notification.EXTRA_LARGE_ICON); extras.remove(Notification.EXTRA_LARGE_ICON_BIG); extras.remove(Notification.EXTRA_PICTURE); + // Prevent light notifications from being rebuilt. + extras.remove(Builder.EXTRA_NEEDS_REBUILD); } } @@ -1868,6 +1870,15 @@ public class Notification implements Parcelable private Notification mRebuildNotification = null; /** + * Whether the build notification has three lines. This is used to make the top padding for + * both the contracted and expanded layout consistent. + * + * <p> + * This field is only valid during the build phase. + */ + private boolean mHasThreeLines; + + /** * Constructs a new Builder with the defaults: * @@ -2564,19 +2575,23 @@ public class Notification implements Parcelable return this; } - private Bitmap getProfileBadge() { + private Drawable getProfileBadgeDrawable() { // Note: This assumes that the current user can read the profile badge of the // originating user. UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - Drawable badge = userManager.getBadgeForUser(new UserHandle(mOriginatingUserId), 0); + return userManager.getBadgeForUser(new UserHandle(mOriginatingUserId), 0); + } + + private Bitmap getProfileBadge() { + Drawable badge = getProfileBadgeDrawable(); if (badge == null) { return null; } - final int width = badge.getIntrinsicWidth(); - final int height = badge.getIntrinsicHeight(); - Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + final int size = mContext.getResources().getDimensionPixelSize( + R.dimen.notification_badge_size); + Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); - badge.setBounds(0, 0, width, height); + badge.setBounds(0, 0, size, size); badge.draw(canvas); return bitmap; } @@ -2602,6 +2617,12 @@ public class Notification implements Parcelable return false; } + private void shrinkLine3Text(RemoteViews contentView) { + float subTextSize = mContext.getResources().getDimensionPixelSize( + R.dimen.notification_subtext_size); + contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize); + } + private RemoteViews applyStandardTemplate(int resId) { RemoteViews contentView = new BuilderRemoteViews(mContext.getPackageName(), mOriginatingUserId, resId); @@ -2674,10 +2695,7 @@ public class Notification implements Parcelable if (showLine2) { // need to shrink all the type to make sure everything fits - final Resources res = mContext.getResources(); - final float subTextSize = res.getDimensionPixelSize( - R.dimen.notification_subtext_size); - contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize); + shrinkLine3Text(contentView); } if (mWhen != 0 && mShowWhen) { @@ -2696,7 +2714,7 @@ public class Notification implements Parcelable // Adjust padding depending on line count and font size. contentView.setViewPadding(R.id.line1, 0, calculateTopPadding(mContext, - hasThreeLines(), mContext.getResources().getConfiguration().fontScale), + mHasThreeLines, mContext.getResources().getConfiguration().fontScale), 0, 0); // We want to add badge to first line of text. @@ -2721,7 +2739,12 @@ public class Notification implements Parcelable * is going to have one or two lines */ private boolean hasThreeLines() { - boolean hasLine3 = mContentText != null || mContentInfo != null || mNumber > 0; + boolean contentTextInLine2 = mSubText != null && mContentText != null; + + // If we have content text in line 2, badge goes into line 2, or line 3 otherwise + boolean badgeInLine3 = getProfileBadgeDrawable() != null && !contentTextInLine2; + boolean hasLine3 = mContentText != null || mContentInfo != null || mNumber > 0 + || badgeInLine3; boolean hasLine2 = (mSubText != null && mContentText != null) || (mSubText == null && (mProgressMax != 0 || mProgressIndeterminate)); return hasLine2 && hasLine3; @@ -3092,6 +3115,7 @@ public class Notification implements Parcelable if (mRebuildNotification == null) { throw new IllegalStateException("rebuild() only valid when in 'rebuild' mode."); } + mHasThreeLines = hasThreeLines(); Bundle extras = mRebuildNotification.extras; @@ -3124,6 +3148,7 @@ public class Notification implements Parcelable } extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW); + mHasThreeLines = false; return mRebuildNotification; } @@ -3238,6 +3263,7 @@ public class Notification implements Parcelable */ public Notification build() { mOriginatingUserId = mContext.getUserId(); + mHasThreeLines = hasThreeLines(); Notification n = buildUnstyled(); @@ -3259,6 +3285,7 @@ public class Notification implements Parcelable mStyle.addExtras(n.extras); } + mHasThreeLines = false; return n; } @@ -3388,7 +3415,7 @@ public class Notification implements Parcelable */ protected void applyTopPadding(RemoteViews contentView) { int topPadding = Builder.calculateTopPadding(mBuilder.mContext, - mBuilder.hasThreeLines(), + mBuilder.mHasThreeLines, mBuilder.mContext.getResources().getConfiguration().fontScale); contentView.setViewPadding(R.id.line1, 0, topPadding, 0, 0); } @@ -3661,6 +3688,8 @@ public class Notification implements Parcelable applyTopPadding(contentView); + mBuilder.shrinkLine3Text(contentView); + mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template); return contentView; @@ -3800,6 +3829,8 @@ public class Notification implements Parcelable applyTopPadding(contentView); + mBuilder.shrinkLine3Text(contentView); + mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template); return contentView; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 26c72a5..69b1139 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2456,10 +2456,10 @@ public class DevicePolicyManager { * @throws IllegalArgumentException if the userId is invalid. */ @SystemApi - public String getProfileOwnerNameAsUser(UserHandle user) throws IllegalArgumentException { + public String getProfileOwnerNameAsUser(int userId) throws IllegalArgumentException { if (mService != null) { try { - return mService.getProfileOwnerName(user.getIdentifier()); + return mService.getProfileOwnerName(userId); } catch (RemoteException re) { Log.w(TAG, "Failed to get profile owner"); throw new IllegalArgumentException( @@ -2562,6 +2562,7 @@ public class DevicePolicyManager { * @param agent Which component to enable features for. * @param features List of features to enable. Consult specific TrustAgent documentation for * the feature list. + * @hide */ public void setTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent, List<String> features) { @@ -2582,6 +2583,7 @@ public class DevicePolicyManager { * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param agent Which component to get enabled features for. * @return List of enabled features. + * @hide */ public List<String> getTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent) { if (mService != null) { @@ -2689,10 +2691,10 @@ public class DevicePolicyManager { * Called by a profile or device owner to set the permitted accessibility services. When * set by a device owner or profile owner the restriction applies to all profiles of the * user the device owner or profile owner is an admin for. - * + * * By default the user can use any accessiblity service. When zero or more packages have * been added, accessiblity services that are not in the list and not part of the system - * can not be enabled by the user. + * can not be enabled by the user. * * <p> Calling with a null value for the list disables the restriction so that all services * can be used, calling with an empty list only allows the builtin system's services. @@ -3325,10 +3327,10 @@ public class DevicePolicyManager { * @param packageName package to check. * @return true if the user shouldn't be able to uninstall the package. */ - public boolean getUninstallBlocked(ComponentName admin, String packageName) { + public boolean isUninstallBlocked(ComponentName admin, String packageName) { if (mService != null) { try { - return mService.getUninstallBlocked(admin, packageName); + return mService.isUninstallBlocked(admin, packageName); } catch (RemoteException re) { Log.w(TAG, "Failed to call block uninstall on device policy service"); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 23f36fb..c984cf9 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -174,7 +174,7 @@ interface IDevicePolicyManager { void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userId); void setUninstallBlocked(in ComponentName admin, in String packageName, boolean uninstallBlocked); - boolean getUninstallBlocked(in ComponentName admin, in String packageName); + boolean isUninstallBlocked(in ComponentName admin, in String packageName); void setCrossProfileCallerIdDisabled(in ComponentName who, boolean disabled); boolean getCrossProfileCallerIdDisabled(in ComponentName who); diff --git a/core/java/android/app/backup/BackupHelper.java b/core/java/android/app/backup/BackupHelper.java index e3f0d54..7cbbbc3 100644 --- a/core/java/android/app/backup/BackupHelper.java +++ b/core/java/android/app/backup/BackupHelper.java @@ -37,10 +37,9 @@ import android.os.ParcelFileDescriptor; */ public interface BackupHelper { /** - * Based on <code>oldState</code>, determine which of the files from the - * application's data directory need to be backed up, write them to - * <code>data</code>, and fill in <code>newState</code> with the state as it - * exists now. + * Based on <code>oldState</code>, determine what application content + * needs to be backed up, write it to <code>data</code>, and fill in + * <code>newState</code> with the complete state as it exists now. * <p> * Implementing this method is much like implementing * {@link BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index 2431ad0..fb80de2 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -106,7 +106,9 @@ public final class UsageEvents implements Parcelable { } /** - * The time at which this event occurred. + * The time at which this event occurred, measured in milliseconds since the epoch. + * <p/> + * See {@link System#currentTimeMillis()}. */ public long getTimeStamp() { return mTimeStamp; diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java index e47a802..abfc435 100644 --- a/core/java/android/app/usage/UsageStats.java +++ b/core/java/android/app/usage/UsageStats.java @@ -81,28 +81,36 @@ public final class UsageStats implements Parcelable { } /** - * Get the beginning of the time range this {@link android.app.usage.UsageStats} represents. + * Get the beginning of the time range this {@link android.app.usage.UsageStats} represents, + * measured in milliseconds since the epoch. + * <p/> + * See {@link System#currentTimeMillis()}. */ public long getFirstTimeStamp() { return mBeginTimeStamp; } /** - * Get the end of the time range this {@link android.app.usage.UsageStats} represents. + * Get the end of the time range this {@link android.app.usage.UsageStats} represents, + * measured in milliseconds since the epoch. + * <p/> + * See {@link System#currentTimeMillis()}. */ public long getLastTimeStamp() { return mEndTimeStamp; } /** - * Get the last time this package was used. + * Get the last time this package was used, measured in milliseconds since the epoch. + * <p/> + * See {@link System#currentTimeMillis()}. */ public long getLastTimeUsed() { return mLastTimeUsed; } /** - * Get the total time this package spent in the foreground. + * Get the total time this package spent in the foreground, measured in milliseconds. */ public long getTotalTimeInForeground() { return mTotalTimeInForeground; diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index f9b8928..5830fcf 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -23,6 +23,7 @@ import android.util.ArrayMap; import java.util.Collections; import java.util.List; +import java.util.Map; /** * Provides access to device usage history and statistics. Usage data is aggregated into @@ -149,7 +150,6 @@ public final class UsageStatsManager { * @param endTime The exclusive end of the range of events to include in the results. * @return A {@link UsageEvents}. */ - @SuppressWarnings("unchecked") public UsageEvents queryEvents(long beginTime, long endTime) { try { UsageEvents iter = mService.queryEvents(beginTime, endTime, @@ -170,15 +170,13 @@ public final class UsageStatsManager { * * @param beginTime The inclusive beginning of the range of stats to include in the results. * @param endTime The exclusive end of the range of stats to include in the results. - * @return An {@link android.util.ArrayMap} keyed by package name or null if no stats are + * @return A {@link java.util.Map} keyed by package name, or null if no stats are * available. */ - public ArrayMap<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) { + public Map<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) { List<UsageStats> stats = queryUsageStats(INTERVAL_BEST, beginTime, endTime); if (stats.isEmpty()) { - @SuppressWarnings("unchecked") - ArrayMap<String, UsageStats> emptyStats = ArrayMap.EMPTY; - return emptyStats; + return Collections.emptyMap(); } ArrayMap<String, UsageStats> aggregatedStats = new ArrayMap<>(); diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index b13792b..b2b48e8 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -27,6 +27,7 @@ import android.database.ContentObserver; import android.database.CrossProcessCursorWrapper; import android.database.Cursor; import android.database.IContentObserver; +import android.graphics.Point; import android.net.Uri; import android.os.Bundle; import android.os.CancellationSignal; @@ -161,6 +162,17 @@ public abstract class ContentResolver { public static final String SCHEME_FILE = "file"; /** + * An extra {@link Point} describing the optimal size for a requested image + * resource, in pixels. If a provider has multiple sizes of the image, it + * should return the image closest to this size. + * + * @see #openTypedAssetFileDescriptor(Uri, String, Bundle) + * @see #openTypedAssetFileDescriptor(Uri, String, Bundle, + * CancellationSignal) + */ + public static final String EXTRA_SIZE = "android.content.extra.SIZE"; + + /** * This is the Android platform's base MIME type for a content: URI * containing a Cursor of a single item. Applications should use this * as the base type along with their own sub-type of their content: URIs diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index c928a18..44e24b1 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -623,7 +623,7 @@ public class PackageInstaller { try { final ParcelFileDescriptor clientSocket = mSession.openWrite(name, offsetBytes, lengthBytes); - return new FileBridge.FileBridgeOutputStream(clientSocket.getFileDescriptor()); + return new FileBridge.FileBridgeOutputStream(clientSocket); } catch (RuntimeException e) { ExceptionUtils.maybeUnwrapIOException(e); throw e; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 5492775..fa2bb4d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1456,6 +1456,15 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports live TV and can display + * contents from TV inputs implemented with the + * {@link android.media.tv.TvInputService} API. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_LIVE_TV = "android.software.live_tv"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device supports WiFi (802.11) networking. */ @SdkConstant(SdkConstantType.FEATURE) diff --git a/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl b/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl index caabed3..ca0935c 100644 --- a/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl +++ b/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl @@ -26,8 +26,8 @@ interface ICameraDeviceCallbacks * Keep up-to-date with frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h */ - oneway void onCameraError(int errorCode, in CaptureResultExtras resultExtras); - oneway void onCameraIdle(); + oneway void onDeviceError(int errorCode, in CaptureResultExtras resultExtras); + oneway void onDeviceIdle(); oneway void onCaptureStarted(in CaptureResultExtras resultExtras, long timestamp); oneway void onResultReceived(in CameraMetadataNative result, in CaptureResultExtras resultExtras); diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java index ec4bc7d..0895fe3 100644 --- a/core/java/android/hardware/camera2/TotalCaptureResult.java +++ b/core/java/android/hardware/camera2/TotalCaptureResult.java @@ -19,6 +19,7 @@ package android.hardware.camera2; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.impl.CaptureResultExtras; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -48,13 +49,23 @@ import java.util.List; */ public final class TotalCaptureResult extends CaptureResult { + private final List<CaptureResult> mPartialResults; + /** - * Takes ownership of the passed-in properties object + * Takes ownership of the passed-in camera metadata and the partial results + * + * @param partials a list of partial results; {@code null} will be substituted for an empty list * @hide */ public TotalCaptureResult(CameraMetadataNative results, CaptureRequest parent, - CaptureResultExtras extras) { + CaptureResultExtras extras, List<CaptureResult> partials) { super(results, parent, extras); + + if (partials == null) { + mPartialResults = new ArrayList<>(); + } else { + mPartialResults = partials; + } } /** @@ -65,6 +76,8 @@ public final class TotalCaptureResult extends CaptureResult { */ public TotalCaptureResult(CameraMetadataNative results, int sequenceId) { super(results, sequenceId); + + mPartialResults = new ArrayList<>(); } /** @@ -73,14 +86,13 @@ public final class TotalCaptureResult extends CaptureResult { * <p>The list is returned is unmodifiable; attempting to modify it will result in a * {@code UnsupportedOperationException} being thrown.</p> * - * <p>The list size will be inclusive between {@code 1} and - * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT}, in ascending order + * <p>The list size will be inclusive between {@code 0} and + * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT}, with elements in ascending order * of when {@link CameraCaptureSession.CaptureListener#onCaptureProgressed} was invoked.</p> * * @return unmodifiable list of partial results */ public List<CaptureResult> getPartialResults() { - // TODO - return Collections.unmodifiableList(null); + return Collections.unmodifiableList(mPartialResults); } } diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index 621968b..9ca1fba 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -74,7 +74,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { private boolean mSkipUnconfigure = false; /** Is the session in the process of aborting? Pay attention to BUSY->IDLE transitions. */ - private boolean mAborting; + private volatile boolean mAborting; /** * Create a new CameraCaptureSession. @@ -346,6 +346,20 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { } /** + * Whether currently in mid-abort. + * + * <p>This is used by the implementation to set the capture failure + * reason, in lieu of more accurate error codes from the camera service. + * Unsynchronized to avoid deadlocks between simultaneous session->device, + * device->session calls.</p> + * + * <p>Package-private.</p> + */ + boolean isAborting() { + return mAborting; + } + + /** * Post calls into a CameraCaptureSession.StateListener to the user-specified {@code handler}. */ private StateListener createUserStateListenerProxy(Handler handler, StateListener listener) { @@ -502,8 +516,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { // TODO: Queue captures during abort instead of failing them // since the app won't be able to distinguish the two actives + // Don't signal the application since there's no clean mapping here Log.w(TAG, "Device is now busy; do not submit new captures (TODO: allow this)"); - mStateListener.onActive(session); } @Override diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 79ce9df..f5666bf 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -41,6 +41,7 @@ import android.view.Surface; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -384,7 +385,7 @@ public class CameraDeviceImpl extends CameraDevice { catch (IllegalArgumentException e) { // OK. camera service can reject stream config if it's not supported by HAL // This is only the result of a programmer misusing the camera2 api. - Log.e(TAG, "Stream configuration failed", e); + Log.w(TAG, "Stream configuration failed"); return false; } @@ -721,6 +722,13 @@ public class CameraDeviceImpl extends CameraDevice { checkIfCameraClosedOrInError(); mDeviceHandler.post(mCallOnBusy); + + // If already idle, just do a busy->idle transition immediately, don't actually + // flush. + if (mIdle) { + mDeviceHandler.post(mCallOnIdle); + return; + } try { LongParcelable lastFrameNumberRef = new LongParcelable(); mRemoteDevice.flush(/*out*/lastFrameNumberRef); @@ -960,6 +968,8 @@ public class CameraDeviceImpl extends CameraDevice { private long mCompletedFrameNumber = -1; private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>(); + /** Map frame numbers to list of partial results */ + private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>(); private void update() { Iterator<Long> iter = mFutureErrorSet.iterator(); @@ -976,8 +986,8 @@ public class CameraDeviceImpl extends CameraDevice { /** * This function is called every time when a result or an error is received. - * @param frameNumber: the frame number corresponding to the result or error - * @param isError: true if it is an error, false if it is not an error + * @param frameNumber the frame number corresponding to the result or error + * @param isError true if it is an error, false if it is not an error */ public void updateTracker(long frameNumber, boolean isError) { if (isError) { @@ -999,6 +1009,55 @@ public class CameraDeviceImpl extends CameraDevice { update(); } + /** + * This function is called every time a result has been completed. + * + * <p>It keeps a track of all the partial results already created for a particular + * frame number.</p> + * + * @param frameNumber the frame number corresponding to the result + * @param result the total or partial result + * @param partial {@true} if the result is partial, {@code false} if total + */ + public void updateTracker(long frameNumber, CaptureResult result, boolean partial) { + + if (!partial) { + // Update the total result's frame status as being successful + updateTracker(frameNumber, /*isError*/false); + // Don't keep a list of total results, we don't need to track them + return; + } + + if (result == null) { + // Do not record blank results; this also means there will be no total result + // so it doesn't matter that the partials were not recorded + return; + } + + // Partial results must be aggregated in-order for that frame number + List<CaptureResult> partials = mPartialResults.get(frameNumber); + if (partials == null) { + partials = new ArrayList<>(); + mPartialResults.put(frameNumber, partials); + } + + partials.add(result); + } + + /** + * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}. + * + * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker} + * is called again with new partials for that frame number).</p> + * + * @param frameNumber the frame number corresponding to the result + * @return a list of partial results for that frame with at least 1 element, + * or {@code null} if there were no partials recorded for that frame + */ + public List<CaptureResult> popPartialResults(long frameNumber) { + return mPartialResults.remove(frameNumber); + } + public long getCompletedFrameNumber() { return mCompletedFrameNumber; } @@ -1097,31 +1156,51 @@ public class CameraDeviceImpl extends CameraDevice { */ static final int ERROR_CAMERA_SERVICE = 2; + /** + * Camera has encountered an error processing a single request. + */ + static final int ERROR_CAMERA_REQUEST = 3; + + /** + * Camera has encountered an error producing metadata for a single capture + */ + static final int ERROR_CAMERA_RESULT = 4; + + /** + * Camera has encountered an error producing an image buffer for a single capture + */ + static final int ERROR_CAMERA_BUFFER = 5; + @Override public IBinder asBinder() { return this; } @Override - public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) { - Runnable r = null; + public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) { + if (DEBUG) { + Log.d(TAG, String.format( + "Device error received, code %d, frame number %d, request ID %d, subseq ID %d", + errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(), + resultExtras.getSubsequenceId())); + } synchronized(mInterfaceLock) { if (mRemoteDevice == null) { return; // Camera already closed } - mInError = true; switch (errorCode) { case ERROR_CAMERA_DISCONNECTED: - r = mCallOnDisconnected; + CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected); break; default: Log.e(TAG, "Unknown error from camera device: " + errorCode); // no break case ERROR_CAMERA_DEVICE: case ERROR_CAMERA_SERVICE: - r = new Runnable() { + mInError = true; + Runnable r = new Runnable() { @Override public void run() { if (!CameraDeviceImpl.this.isClosed()) { @@ -1129,21 +1208,19 @@ public class CameraDeviceImpl extends CameraDevice { } } }; + CameraDeviceImpl.this.mDeviceHandler.post(r); + break; + case ERROR_CAMERA_REQUEST: + case ERROR_CAMERA_RESULT: + case ERROR_CAMERA_BUFFER: + onCaptureErrorLocked(errorCode, resultExtras); break; } - CameraDeviceImpl.this.mDeviceHandler.post(r); - - // Fire onCaptureSequenceCompleted - if (DEBUG) { - Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber())); - } - mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true); - checkAndFireSequenceComplete(); } } @Override - public void onCameraIdle() { + public void onDeviceIdle() { if (DEBUG) { Log.d(TAG, "Camera now idle"); } @@ -1219,12 +1296,6 @@ public class CameraDeviceImpl extends CameraDevice { boolean isPartialResult = (resultExtras.getPartialResultCount() < mTotalPartialCount); - // Update tracker (increment counter) when it's not a partial result. - if (!isPartialResult) { - mFrameNumberTracker.updateTracker(frameNumber, - /*error*/false); - } - // Check if we have a listener for this if (holder == null) { if (DEBUG) { @@ -1232,6 +1303,9 @@ public class CameraDeviceImpl extends CameraDevice { "holder is null, early return at frame " + frameNumber); } + + mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult); + return; } @@ -1241,14 +1315,17 @@ public class CameraDeviceImpl extends CameraDevice { "camera is closed, early return at frame " + frameNumber); } + + mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult); return; } final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId()); - Runnable resultDispatch = null; + CaptureResult finalResult; + // Either send a partial result or the final capture completed result if (isPartialResult) { final CaptureResult resultAsCapture = @@ -1266,9 +1343,14 @@ public class CameraDeviceImpl extends CameraDevice { } } }; + + finalResult = resultAsCapture; } else { + List<CaptureResult> partialResults = + mFrameNumberTracker.popPartialResults(frameNumber); + final TotalCaptureResult resultAsCapture = - new TotalCaptureResult(result, request, resultExtras); + new TotalCaptureResult(result, request, resultExtras, partialResults); // Final capture result resultDispatch = new Runnable() { @@ -1282,19 +1364,80 @@ public class CameraDeviceImpl extends CameraDevice { } } }; + + finalResult = resultAsCapture; } holder.getHandler().post(resultDispatch); + // Collect the partials for a total result; or mark the frame as totally completed + mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult); + // Fire onCaptureSequenceCompleted if (!isPartialResult) { checkAndFireSequenceComplete(); } + } + } + + /** + * Called by onDeviceError for handling single-capture failures. + */ + private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) { + + final int requestId = resultExtras.getRequestId(); + final int subsequenceId = resultExtras.getSubsequenceId(); + final long frameNumber = resultExtras.getFrameNumber(); + final CaptureListenerHolder holder = + CameraDeviceImpl.this.mCaptureListenerMap.get(requestId); + + final CaptureRequest request = holder.getRequest(subsequenceId); + + // No way to report buffer errors right now + if (errorCode == ERROR_CAMERA_BUFFER) { + Log.e(TAG, String.format("Lost output buffer reported for frame %d", frameNumber)); + return; + } + + boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT); + + // This is only approximate - exact handling needs the camera service and HAL to + // disambiguate between request failures to due abort and due to real errors. + // For now, assume that if the session believes we're mid-abort, then the error + // is due to abort. + int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ? + CaptureFailure.REASON_FLUSHED : + CaptureFailure.REASON_ERROR; + + final CaptureFailure failure = new CaptureFailure( + request, + reason, + /*dropped*/ mayHaveBuffers, + requestId, + frameNumber); + + Runnable failureDispatch = new Runnable() { + @Override + public void run() { + if (!CameraDeviceImpl.this.isClosed()){ + holder.getListener().onCaptureFailed( + CameraDeviceImpl.this, + request, + failure); + } + } + }; + holder.getHandler().post(failureDispatch); + // Fire onCaptureSequenceCompleted if appropriate + if (DEBUG) { + Log.v(TAG, String.format("got error frame %d", frameNumber)); } + mFrameNumberTracker.updateTracker(frameNumber, /*error*/true); + checkAndFireSequenceComplete(); } - } + } // public class CameraDeviceCallbacks /** * Default handler management. diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index febb015..f47ce79 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -67,7 +67,6 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.HashMap; -import java.util.List; /** * Implementation of camera metadata marshal/unmarshal across Binder to @@ -655,6 +654,15 @@ public class CameraMetadataNative implements Parcelable { private Face[] getFaces() { Integer faceDetectMode = get(CaptureResult.STATISTICS_FACE_DETECT_MODE); + byte[] faceScores = get(CaptureResult.STATISTICS_FACE_SCORES); + Rect[] faceRectangles = get(CaptureResult.STATISTICS_FACE_RECTANGLES); + int[] faceIds = get(CaptureResult.STATISTICS_FACE_IDS); + int[] faceLandmarks = get(CaptureResult.STATISTICS_FACE_LANDMARKS); + + if (areValuesAllNull(faceDetectMode, faceScores, faceRectangles, faceIds, faceLandmarks)) { + return null; + } + if (faceDetectMode == null) { Log.w(TAG, "Face detect mode metadata is null, assuming the mode is SIMPLE"); faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE; @@ -670,8 +678,6 @@ public class CameraMetadataNative implements Parcelable { } // Face scores and rectangles are required by SIMPLE and FULL mode. - byte[] faceScores = get(CaptureResult.STATISTICS_FACE_SCORES); - Rect[] faceRectangles = get(CaptureResult.STATISTICS_FACE_RECTANGLES); if (faceScores == null || faceRectangles == null) { Log.w(TAG, "Expect face scores and rectangles to be non-null"); return new Face[0]; @@ -683,8 +689,6 @@ public class CameraMetadataNative implements Parcelable { // To be safe, make number of faces is the minimal of all face info metadata length. int numFaces = Math.min(faceScores.length, faceRectangles.length); // Face id and landmarks are only required by FULL mode. - int[] faceIds = get(CaptureResult.STATISTICS_FACE_IDS); - int[] faceLandmarks = get(CaptureResult.STATISTICS_FACE_LANDMARKS); if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) { if (faceIds == null || faceLandmarks == null) { Log.w(TAG, "Expect face ids and landmarks to be non-null for FULL mode," + @@ -755,22 +759,32 @@ public class CameraMetadataNative implements Parcelable { private LensShadingMap getLensShadingMap() { float[] lsmArray = getBase(CaptureResult.STATISTICS_LENS_SHADING_MAP); + Size s = get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE); + + // Do not warn if lsmArray is null while s is not. This is valid. if (lsmArray == null) { - Log.w(TAG, "getLensShadingMap - Lens shading map was null."); return null; } - Size s = get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE); + + if (s == null) { + Log.w(TAG, "getLensShadingMap - Lens shading map size was null."); + return null; + } + LensShadingMap map = new LensShadingMap(lsmArray, s.getHeight(), s.getWidth()); return map; } private Location getGpsLocation() { String processingMethod = get(CaptureResult.JPEG_GPS_PROCESSING_METHOD); - Location l = new Location(translateProcessToLocationProvider(processingMethod)); - double[] coords = get(CaptureResult.JPEG_GPS_COORDINATES); Long timeStamp = get(CaptureResult.JPEG_GPS_TIMESTAMP); + if (areValuesAllNull(processingMethod, coords, timeStamp)) { + return null; + } + + Location l = new Location(translateProcessToLocationProvider(processingMethod)); if (timeStamp != null) { l.setTime(timeStamp); } else { @@ -873,7 +887,13 @@ public class CameraMetadataNative implements Parcelable { float[] red = getBase(CaptureRequest.TONEMAP_CURVE_RED); float[] green = getBase(CaptureRequest.TONEMAP_CURVE_GREEN); float[] blue = getBase(CaptureRequest.TONEMAP_CURVE_BLUE); + + if (areValuesAllNull(red, green, blue)) { + return null; + } + if (red == null || green == null || blue == null) { + Log.w(TAG, "getTonemapCurve - missing tone curve components"); return null; } TonemapCurve tc = new TonemapCurve(red, green, blue); @@ -1208,6 +1228,18 @@ public class CameraMetadataNative implements Parcelable { } } + /** Check if input arguments are all {@code null}. + * + * @param objs Input arguments for null check + * @return {@code true} if input arguments are all {@code null}, otherwise {@code false} + */ + private static boolean areValuesAllNull(Object... objs) { + for (Object o : objs) { + if (o != null) return false; + } + return true; + } + static { /* * We use a class initializer to allow the native code to cache some field offsets diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java index 5cbf109..410934e 100644 --- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java +++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java @@ -211,7 +211,7 @@ public class CameraDeviceUserShim implements ICameraDeviceUser { } @Override - public void onCameraError(final int errorCode, final CaptureResultExtras resultExtras) { + public void onDeviceError(final int errorCode, final CaptureResultExtras resultExtras) { Message msg = getHandler().obtainMessage(CAMERA_ERROR, /*arg1*/ errorCode, /*arg2*/ 0, /*obj*/ resultExtras); @@ -219,7 +219,7 @@ public class CameraDeviceUserShim implements ICameraDeviceUser { } @Override - public void onCameraIdle() { + public void onDeviceIdle() { Message msg = getHandler().obtainMessage(CAMERA_IDLE); getHandler().sendMessage(msg); } @@ -267,11 +267,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser { case CAMERA_ERROR: { int errorCode = msg.arg1; CaptureResultExtras resultExtras = (CaptureResultExtras) msg.obj; - mCallbacks.onCameraError(errorCode, resultExtras); + mCallbacks.onDeviceError(errorCode, resultExtras); break; } case CAMERA_IDLE: - mCallbacks.onCameraIdle(); + mCallbacks.onDeviceIdle(); break; case CAPTURE_STARTED: { long timestamp = msg.arg2 & 0xFFFFFFFFL; diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java index 1cf7797..ffc55f1 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java +++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java @@ -97,7 +97,7 @@ public class LegacyCameraDevice implements AutoCloseable { Log.d(TAG, "doing onError callback."); } try { - mDeviceCallbacks.onCameraError(errorCode, extras); + mDeviceCallbacks.onDeviceError(errorCode, extras); } catch (RemoteException e) { throw new IllegalStateException( "Received remote exception during onCameraError callback: ", e); @@ -125,7 +125,7 @@ public class LegacyCameraDevice implements AutoCloseable { Log.d(TAG, "doing onIdle callback."); } try { - mDeviceCallbacks.onCameraIdle(); + mDeviceCallbacks.onDeviceIdle(); } catch (RemoteException e) { throw new IllegalStateException( "Received remote exception during onCameraIdle callback: ", e); diff --git a/core/java/android/hardware/hdmi/HdmiPortInfo.java b/core/java/android/hardware/hdmi/HdmiPortInfo.java index 85e7531..2ec6126 100644 --- a/core/java/android/hardware/hdmi/HdmiPortInfo.java +++ b/core/java/android/hardware/hdmi/HdmiPortInfo.java @@ -166,7 +166,7 @@ public final class HdmiPortInfo implements Parcelable { public String toString() { StringBuffer s = new StringBuffer(); s.append("port_id: ").append(mId).append(", "); - s.append("address: ").append(mAddress).append(", "); + s.append("address: ").append(String.format("0x%04x", mAddress)).append(", "); s.append("cec: ").append(mCecSupported).append(", "); s.append("arc: ").append(mArcSupported).append(", "); s.append("mhl: ").append(mMhlSupported); diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index 22da90e..8df9916 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -45,7 +45,7 @@ public abstract class NetworkAgent extends Handler { private volatile AsyncChannel mAsyncChannel; private final String LOG_TAG; private static final boolean DBG = true; - private static final boolean VDBG = true; + private static final boolean VDBG = false; private final Context mContext; private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>(); @@ -134,7 +134,7 @@ public abstract class NetworkAgent extends Handler { throw new IllegalArgumentException(); } - if (DBG) log("Registering NetworkAgent"); + if (VDBG) log("Registering NetworkAgent"); ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService( Context.CONNECTIVITY_SERVICE); cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni), @@ -148,7 +148,7 @@ public abstract class NetworkAgent extends Handler { if (mAsyncChannel != null) { log("Received new connection while already connected!"); } else { - if (DBG) log("NetworkAgent fully connected"); + if (VDBG) log("NetworkAgent fully connected"); AsyncChannel ac = new AsyncChannel(); ac.connected(null, this, msg.replyTo); ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, @@ -164,7 +164,7 @@ public abstract class NetworkAgent extends Handler { break; } case AsyncChannel.CMD_CHANNEL_DISCONNECT: { - if (DBG) log("CMD_CHANNEL_DISCONNECT"); + if (VDBG) log("CMD_CHANNEL_DISCONNECT"); if (mAsyncChannel != null) mAsyncChannel.disconnect(); break; } diff --git a/core/java/android/net/NetworkBoundURLFactory.java b/core/java/android/net/NetworkBoundURLFactory.java deleted file mode 100644 index 356100e..0000000 --- a/core/java/android/net/NetworkBoundURLFactory.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2014 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.net; - -import java.net.MalformedURLException; -import java.net.URL; - -/** - * An interface that describes a factory for network-specific {@link URL} objects. - */ -public interface NetworkBoundURLFactory { - /** - * Returns a {@link URL} based on the given URL but bound to the specified {@code Network}, - * such that opening the URL will send all network traffic on the specified Network. - * - * @return a {@link URL} bound to this {@code Network}. - * @throws MalformedURLException if the URL was not valid, or this factory cannot handle the - * specified URL (e.g., if it does not support the protocol of the URL). - */ - public URL getBoundURL(Network network, URL url) throws MalformedURLException; -} diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java index a20e8e7..6ddd8b3 100644 --- a/core/java/android/net/NetworkFactory.java +++ b/core/java/android/net/NetworkFactory.java @@ -46,6 +46,7 @@ import com.android.internal.util.Protocol; **/ public class NetworkFactory extends Handler { private static final boolean DBG = true; + private static final boolean VDBG = false; private static final int BASE = Protocol.BASE_NETWORK_FACTORY; /** @@ -164,13 +165,14 @@ public class NetworkFactory extends Handler { private void handleAddRequest(NetworkRequest request, int score) { NetworkRequestInfo n = mNetworkRequests.get(request.requestId); if (n == null) { + if (DBG) log("got request " + request + " with score " + score); n = new NetworkRequestInfo(request, score); mNetworkRequests.put(n.request.requestId, n); } else { + if (VDBG) log("new score " + score + " for exisiting request " + request); n.score = score; } - if (DBG) log("got request " + request + " with score " + score); - if (DBG) log(" my score=" + mScore + ", my filter=" + mCapabilityFilter); + if (VDBG) log(" my score=" + mScore + ", my filter=" + mCapabilityFilter); evalRequest(n); } diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java index bf8d15c..022a106 100644 --- a/core/java/android/os/FileBridge.java +++ b/core/java/android/os/FileBridge.java @@ -131,10 +131,17 @@ public class FileBridge extends Thread { } public static class FileBridgeOutputStream extends OutputStream { + private final ParcelFileDescriptor mClientPfd; private final FileDescriptor mClient; private final byte[] mTemp = new byte[MSG_LENGTH]; + public FileBridgeOutputStream(ParcelFileDescriptor clientPfd) { + mClientPfd = clientPfd; + mClient = clientPfd.getFileDescriptor(); + } + public FileBridgeOutputStream(FileDescriptor client) { + mClientPfd = null; mClient = client; } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index f7b0ead..00e2e22 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -248,7 +248,7 @@ public final class PowerManager { * {@link #PROXIMITY_SCREEN_OFF_WAKE_LOCK} wake lock until the proximity sensor * indicates that an object is not in close proximity. */ - public static final int WAIT_FOR_PROXIMITY_NEGATIVE = 1; + public static final int WAIT_FOR_DISTANT_PROXIMITY = 1; /** * Brightness value for fully on. @@ -961,7 +961,8 @@ public final class PowerManager { * </p> * * @param flags Combination of flag values to modify the release behavior. - * Currently only {@link #WAIT_FOR_PROXIMITY_NEGATIVE} is supported. + * Currently only {@link #WAIT_FOR_DISTANT_PROXIMITY} is supported. Passing 0 is + * equivalent to calling {@link #release()}. */ public void release(int flags) { synchronized (mToken) { diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 942da5a..0202f91 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -116,7 +116,8 @@ public class CallLog { * </pre> * </p> */ - public static final String EXTRA_CALL_TYPE_FILTER = "extra_call_type_filter"; + public static final String EXTRA_CALL_TYPE_FILTER + = "android.provider.extra.call_type_filter"; /** * Content uri used to access call log entries, including voicemail records. You must have @@ -349,6 +350,15 @@ public class CallLog { public static final String PHONE_ACCOUNT_ID = "subscription_id"; /** + * The identifier of a account that is unique to a specified component. Equivalent value + * to {@link #PHONE_ACCOUNT_ID}. For ContactsProvider internal use only. + * <P>Type: INTEGER</P> + * + * @hide + */ + public static final String SUB_ID = "sub_id"; + + /** * If a successful call is made that is longer than this duration, update the phone number * in the ContactsProvider with the normalized version of the number, based on the user's * current country code. diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 327fe4a..9a0858a 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -511,8 +511,6 @@ public final class DocumentsContract { public static final String METHOD_DELETE_DOCUMENT = "android:deleteDocument"; /** {@hide} */ - public static final String EXTRA_THUMBNAIL_SIZE = "thumbnail_size"; - /** {@hide} */ public static final String EXTRA_URI = "uri"; private static final String PATH_ROOT = "root"; @@ -819,7 +817,7 @@ public final class DocumentsContract { ContentProviderClient client, Uri documentUri, Point size, CancellationSignal signal) throws RemoteException, IOException { final Bundle openOpts = new Bundle(); - openOpts.putParcelable(DocumentsContract.EXTRA_THUMBNAIL_SIZE, size); + openOpts.putParcelable(ContentResolver.EXTRA_SIZE, size); AssetFileDescriptor afd = null; Bitmap bitmap = null; diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index 021fff4..270d786 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -16,7 +16,6 @@ package android.provider; -import static android.provider.DocumentsContract.EXTRA_THUMBNAIL_SIZE; import static android.provider.DocumentsContract.METHOD_CREATE_DOCUMENT; import static android.provider.DocumentsContract.METHOD_DELETE_DOCUMENT; import static android.provider.DocumentsContract.METHOD_RENAME_DOCUMENT; @@ -763,8 +762,8 @@ public abstract class DocumentsProvider extends ContentProvider { public final AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts) throws FileNotFoundException { enforceTree(uri); - if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) { - final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE); + if (opts != null && opts.containsKey(ContentResolver.EXTRA_SIZE)) { + final Point sizeHint = opts.getParcelable(ContentResolver.EXTRA_SIZE); return openDocumentThumbnail(getDocumentId(uri), sizeHint, null); } else { return super.openTypedAssetFile(uri, mimeTypeFilter, opts); @@ -781,8 +780,8 @@ public abstract class DocumentsProvider extends ContentProvider { Uri uri, String mimeTypeFilter, Bundle opts, CancellationSignal signal) throws FileNotFoundException { enforceTree(uri); - if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) { - final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE); + if (opts != null && opts.containsKey(ContentResolver.EXTRA_SIZE)) { + final Point sizeHint = opts.getParcelable(ContentResolver.EXTRA_SIZE); return openDocumentThumbnail(getDocumentId(uri), sizeHint, signal); } else { return super.openTypedAssetFile(uri, mimeTypeFilter, opts, signal); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 95d1351..ae11f47 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2113,6 +2113,13 @@ public final class Settings { public static final String VOLUME_MASTER_MUTE = "volume_master_mute"; /** + * Microphone mute (int 1 = mute, 0 = not muted). + * + * @hide + */ + public static final String MICROPHONE_MUTE = "microphone_mute"; + + /** * Whether the notifications should use the ring volume (value of 1) or * a separate notification volume (value of 0). In most cases, users * will have this enabled so the notification and ringer volumes will be diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index 93b2d3b..8ca9b6c 100644 --- a/core/java/android/service/notification/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -29,4 +29,5 @@ oneway interface INotificationListener in NotificationRankingUpdate update); void onNotificationRankingUpdate(in NotificationRankingUpdate update); void onListenerHintsChanged(int hints); -}
\ No newline at end of file + void onInterruptionFilterChanged(int interruptionFilter); +} diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 450b9a7..cb0bcf2 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -58,26 +58,55 @@ public abstract class NotificationListenerService extends Service { private final String TAG = NotificationListenerService.class.getSimpleName() + "[" + getClass().getSimpleName() + "]"; - /** {@link #getCurrentListenerHints() Listener hints} constant - default state. */ - public static final int HINTS_NONE = 0; - - /** Bitmask range for {@link #getCurrentListenerHints() Listener hints} host interruption level - * constants. */ - public static final int HOST_INTERRUPTION_LEVEL_MASK = 0x3; - - /** {@link #getCurrentListenerHints() Listener hints} constant - Normal interruption level. */ - public static final int HINT_HOST_INTERRUPTION_LEVEL_ALL = 1; + /** + * {@link #getCurrentInterruptionFilter() Interruption filter} constant - + * Normal interruption filter. + */ + public static final int INTERRUPTION_FILTER_ALL = 1; - /** {@link #getCurrentListenerHints() Listener hints} constant - Priority interruption level. */ - public static final int HINT_HOST_INTERRUPTION_LEVEL_PRIORITY = 2; + /** + * {@link #getCurrentInterruptionFilter() Interruption filter} constant - + * Priority interruption filter. + */ + public static final int INTERRUPTION_FILTER_PRIORITY = 2; - /** {@link #getCurrentListenerHints() Listener hints} constant - No interruptions level. */ - public static final int HINT_HOST_INTERRUPTION_LEVEL_NONE = 3; + /** + * {@link #getCurrentInterruptionFilter() Interruption filter} constant - + * No interruptions filter. + */ + public static final int INTERRUPTION_FILTER_NONE = 3; /** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI * should disable notification sound, vibrating and other visual or aural effects. - * This does not change the interruption level, only the effects. **/ - public static final int HINT_HOST_DISABLE_EFFECTS = 1 << 2; + * This does not change the interruption filter, only the effects. **/ + public static final int HINT_HOST_DISABLE_EFFECTS = 1; + + /** + * The full trim of the StatusBarNotification including all its features. + * + * @hide + */ + @SystemApi + public static final int TRIM_FULL = 0; + + /** + * A light trim of the StatusBarNotification excluding the following features: + * + * <ol> + * <li>{@link Notification#tickerView tickerView}</li> + * <li>{@link Notification#contentView contentView}</li> + * <li>{@link Notification#largeIcon largeIcon}</li> + * <li>{@link Notification#bigContentView bigContentView}</li> + * <li>{@link Notification#headsUpContentView headsUpContentView}</li> + * <li>{@link Notification#EXTRA_LARGE_ICON extras[EXTRA_LARGE_ICON]}</li> + * <li>{@link Notification#EXTRA_LARGE_ICON_BIG extras[EXTRA_LARGE_ICON_BIG]}</li> + * <li>{@link Notification#EXTRA_PICTURE extras[EXTRA_PICTURE]}</li> + * </ol> + * + * @hide + */ + @SystemApi + public static final int TRIM_LIGHT = 1; private INotificationListenerWrapper mWrapper = null; private RankingMap mRankingMap; @@ -197,6 +226,17 @@ public abstract class NotificationListenerService extends Service { // optional } + /** + * Implement this method to be notified when the + * {@link #getCurrentInterruptionFilter() interruption filter} changed. + * + * @param interruptionFilter The current + * {@link #getCurrentInterruptionFilter() interruption filter}. + */ + public void onInterruptionFilterChanged(int interruptionFilter) { + // optional + } + private final INotificationManager getNotificationInterface() { if (mNoMan == null) { mNoMan = INotificationManager.Stub.asInterface( @@ -301,13 +341,53 @@ public abstract class NotificationListenerService extends Service { } /** + * Sets the notification trim that will be received via {@link #onNotificationPosted}. + * + * <p> + * Setting a trim other than {@link #TRIM_FULL} enables listeners that don't need access to the + * full notification features right away to reduce their memory footprint. Full notifications + * can be requested on-demand via {@link #getActiveNotifications(int)}. + * + * <p> + * Set to {@link #TRIM_FULL} initially. + * + * @hide + * + * @param trim trim of the notifications to be passed via {@link #onNotificationPosted}. + * See <code>TRIM_*</code> constants. + */ + @SystemApi + public final void setOnNotificationPostedTrim(int trim) { + if (!isBound()) return; + try { + getNotificationInterface().setOnNotificationPostedTrimFromListener(mWrapper, trim); + } catch (RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + } + } + + /** * Request the list of outstanding notifications (that is, those that are visible to the * current user). Useful when you don't know what's already been posted. * * @return An array of active notifications, sorted in natural order. */ public StatusBarNotification[] getActiveNotifications() { - return getActiveNotifications(null); + return getActiveNotifications(null, TRIM_FULL); + } + + /** + * Request the list of outstanding notifications (that is, those that are visible to the + * current user). Useful when you don't know what's already been posted. + * + * @hide + * + * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants. + * @return An array of active notifications, sorted in natural order. + */ + @SystemApi + public StatusBarNotification[] getActiveNotifications(int trim) { + return getActiveNotifications(null, trim); } /** @@ -315,14 +395,33 @@ public abstract class NotificationListenerService extends Service { * notifications but didn't want to retain the bits, and now need to go back and extract * more data out of those notifications. * + * @param keys the keys of the notifications to request * @return An array of notifications corresponding to the requested keys, in the * same order as the key list. */ public StatusBarNotification[] getActiveNotifications(String[] keys) { - if (!isBound()) return null; + return getActiveNotifications(keys, TRIM_FULL); + } + + /** + * Request one or more notifications by key. Useful if you have been keeping track of + * notifications but didn't want to retain the bits, and now need to go back and extract + * more data out of those notifications. + * + * @hide + * + * @param keys the keys of the notifications to request + * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants. + * @return An array of notifications corresponding to the requested keys, in the + * same order as the key list. + */ + @SystemApi + public StatusBarNotification[] getActiveNotifications(String[] keys, int trim) { + if (!isBound()) + return null; try { - ParceledListSlice<StatusBarNotification> parceledList = - getNotificationInterface().getActiveNotificationsFromListener(mWrapper, keys); + ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface() + .getActiveNotificationsFromListener(mWrapper, keys, trim); List<StatusBarNotification> list = parceledList.getList(); int N = list.size(); @@ -345,15 +444,42 @@ public abstract class NotificationListenerService extends Service { * shared across all listeners or a feature the notification host does not support or refuses * to grant. * - * @return One or more of the HINT_ constants. + * @return Zero or more of the HINT_ constants. */ public final int getCurrentListenerHints() { - if (!isBound()) return HINTS_NONE; + if (!isBound()) return 0; try { return getNotificationInterface().getHintsFromListener(mWrapper); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); - return HINTS_NONE; + return 0; + } + } + + /** + * Gets the current notification interruption filter active on the host. + * + * <p> + * The interruption filter defines which notifications are allowed to interrupt the user + * (e.g. via sound & vibration) and is applied globally. Listeners can find out whether + * a specific notification matched the interruption filter via + * {@link Ranking#matchesInterruptionFilter()}. + * <p> + * The current filter may differ from the previously requested filter if the notification host + * does not support or refuses to apply the requested filter, or if another component changed + * the filter in the meantime. + * <p> + * Listen for updates using {@link #onInterruptionFilterChanged(int)}. + * + * @return One of the INTERRUPTION_FILTER_ constants, or 0 on errors. + */ + public final int getCurrentInterruptionFilter() { + if (!isBound()) return 0; + try { + return getNotificationInterface().getHintsFromListener(mWrapper); + } catch (android.os.RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + return 0; } } @@ -361,7 +487,7 @@ public abstract class NotificationListenerService extends Service { * Sets the desired {@link #getCurrentListenerHints() listener hints}. * * <p> - * This is merely a request, the host may or not choose to take action depending + * This is merely a request, the host may or may not choose to take action depending * on other listener requests or other global state. * <p> * Listen for updates using {@link #onListenerHintsChanged(int)}. @@ -378,6 +504,27 @@ public abstract class NotificationListenerService extends Service { } /** + * Sets the desired {@link #getCurrentInterruptionFilter() interruption filter}. + * + * <p> + * This is merely a request, the host may or may not choose to apply the requested + * interruption filter depending on other listener requests or other global state. + * <p> + * Listen for updates using {@link #onInterruptionFilterChanged(int)}. + * + * @param interruptionFilter One of the INTERRUPTION_FILTER_ constants. + */ + public final void requestInterruptionFilter(int interruptionFilter) { + if (!isBound()) return; + try { + getNotificationInterface() + .requestInterruptionFilterFromListener(mWrapper, interruptionFilter); + } catch (android.os.RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + } + } + + /** * Returns current ranking information. * * <p> @@ -514,6 +661,15 @@ public abstract class NotificationListenerService extends Service { Log.w(TAG, "Error running onListenerHintsChanged", t); } } + + @Override + public void onInterruptionFilterChanged(int interruptionFilter) throws RemoteException { + try { + NotificationListenerService.this.onInterruptionFilterChanged(interruptionFilter); + } catch (Throwable t) { + Log.w(TAG, "Error running onInterruptionFilterChanged", t); + } + } } private void applyUpdate(NotificationRankingUpdate update) { diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java index 337ae60..3ef5b37 100644 --- a/core/java/android/service/trust/TrustAgentService.java +++ b/core/java/android/service/trust/TrustAgentService.java @@ -89,6 +89,7 @@ public class TrustAgentService extends Service { /** * A white list of features that the given trust agent should support when otherwise disabled * by device policy. + * @hide */ public static final String KEY_FEATURES = "trust_agent_features"; @@ -184,6 +185,7 @@ public class TrustAgentService extends Service { * * @param options Option feature bundle. * @return true if the {@link TrustAgentService} supports this feature. + * @hide */ public boolean onSetTrustAgentFeaturesEnabled(Bundle options) { return false; diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 2095773..519bc28 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -19,6 +19,7 @@ package android.service.voice; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.Activity; import android.content.Intent; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.KeyphraseEnrollmentInfo; @@ -84,20 +85,31 @@ public class AlwaysOnHotwordDetector { private static final int STATE_NOT_READY = 0; // Keyphrase management actions. Used in getManageIntent() ----// - /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { MANAGE_ACTION_ENROLL, MANAGE_ACTION_RE_ENROLL, MANAGE_ACTION_UN_ENROLL }) - public @interface ManageActions {} + private @interface ManageActions {} - /** Indicates that we need to enroll. */ + /** + * Indicates that we need to enroll. + * + * @hide + */ public static final int MANAGE_ACTION_ENROLL = 0; - /** Indicates that we need to re-enroll. */ + /** + * Indicates that we need to re-enroll. + * + * @hide + */ public static final int MANAGE_ACTION_RE_ENROLL = 1; - /** Indicates that we need to un-enroll. */ + /** + * Indicates that we need to un-enroll. + * + * @hide + */ public static final int MANAGE_ACTION_UN_ENROLL = 2; //-- Flags for startRecognition ----// @@ -111,7 +123,11 @@ public class AlwaysOnHotwordDetector { }) public @interface RecognitionFlags {} - /** Empty flag for {@link #startRecognition(int)}. */ + /** + * Empty flag for {@link #startRecognition(int)}. + * + * @hide + */ public static final int RECOGNITION_FLAG_NONE = 0; /** * Recognition flag for {@link #startRecognition(int)} that indicates @@ -264,7 +280,7 @@ public class AlwaysOnHotwordDetector { /** * Callbacks for always-on hotword detection. */ - public interface Callback { + public static abstract class Callback { /** * Called when the hotword availability changes. * This indicates a change in the availability of recognition for the given keyphrase. @@ -278,7 +294,7 @@ public class AlwaysOnHotwordDetector { * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNENROLLED * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_ENROLLED */ - void onAvailabilityChanged(int status); + public abstract void onAvailabilityChanged(int status); /** * Called when the keyphrase is spoken. * This implicitly stops listening for the keyphrase once it's detected. @@ -289,23 +305,23 @@ public class AlwaysOnHotwordDetector { * This may contain the trigger audio, if requested when calling * {@link AlwaysOnHotwordDetector#startRecognition(int)}. */ - void onDetected(@NonNull EventPayload eventPayload); + public abstract void onDetected(@NonNull EventPayload eventPayload); /** * Called when the detection fails due to an error. */ - void onError(); + public abstract void onError(); /** * Called when the recognition is paused temporarily for some reason. * This is an informational callback, and the clients shouldn't be doing anything here * except showing an indication on their UI if they have to. */ - void onRecognitionPaused(); + public abstract void onRecognitionPaused(); /** * Called when the recognition is resumed after it was temporarily paused. * This is an informational callback, and the clients shouldn't be doing anything here * except showing an indication on their UI if they have to. */ - void onRecognitionResumed(); + public abstract void onRecognitionResumed(); } /** @@ -372,10 +388,10 @@ public class AlwaysOnHotwordDetector { /** * Starts recognition for the associated keyphrase. * + * @see #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO + * @see #RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS + * * @param recognitionFlags The flags to control the recognition properties. - * The allowed flags are {@link #RECOGNITION_FLAG_NONE}, - * {@link #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO} and - * {@link #RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS}. * @return Indicates whether the call succeeded or not. * @throws UnsupportedOperationException if the recognition isn't supported. * Callers should only call this method after a supported state callback on @@ -430,12 +446,34 @@ public class AlwaysOnHotwordDetector { } /** - * Gets an intent to manage the associated keyphrase. + * Creates an intent to start the enrollment for the associated keyphrase. + * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}. + * Starting re-enrollment is only valid if the keyphrase is un-enrolled, + * i.e. {@link #STATE_KEYPHRASE_UNENROLLED}, + * otherwise {@link #createIntentToReEnroll()} should be preferred. + * + * @return An {@link Intent} to start enrollment for the given keyphrase. + * @throws UnsupportedOperationException if managing they keyphrase isn't supported. + * Callers should only call this method after a supported state callback on + * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. + * @throws IllegalStateException if the detector is in an invalid state. + * This may happen if another detector has been instantiated or the + * {@link VoiceInteractionService} hosting this detector has been shut down. + */ + public Intent createIntentToEnroll() { + if (DBG) Slog.d(TAG, "createIntentToEnroll"); + synchronized (mLock) { + return getManageIntentLocked(MANAGE_ACTION_ENROLL); + } + } + + /** + * Creates an intent to start the un-enrollment for the associated keyphrase. + * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}. + * Starting re-enrollment is only valid if the keyphrase is already enrolled, + * i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error. * - * @param action The manage action that needs to be performed. - * One of {@link #MANAGE_ACTION_ENROLL}, {@link #MANAGE_ACTION_RE_ENROLL} or - * {@link #MANAGE_ACTION_UN_ENROLL}. - * @return An {@link Intent} to manage the given keyphrase. + * @return An {@link Intent} to start un-enrollment for the given keyphrase. * @throws UnsupportedOperationException if managing they keyphrase isn't supported. * Callers should only call this method after a supported state callback on * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. @@ -443,10 +481,31 @@ public class AlwaysOnHotwordDetector { * This may happen if another detector has been instantiated or the * {@link VoiceInteractionService} hosting this detector has been shut down. */ - public Intent getManageIntent(@ManageActions int action) { - if (DBG) Slog.d(TAG, "getManageIntent(" + action + ")"); + public Intent createIntentToUnEnroll() { + if (DBG) Slog.d(TAG, "createIntentToUnEnroll"); synchronized (mLock) { - return getManageIntentLocked(action); + return getManageIntentLocked(MANAGE_ACTION_UN_ENROLL); + } + } + + /** + * Creates an intent to start the re-enrollment for the associated keyphrase. + * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}. + * Starting re-enrollment is only valid if the keyphrase is already enrolled, + * i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error. + * + * @return An {@link Intent} to start re-enrollment for the given keyphrase. + * @throws UnsupportedOperationException if managing they keyphrase isn't supported. + * Callers should only call this method after a supported state callback on + * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. + * @throws IllegalStateException if the detector is in an invalid state. + * This may happen if another detector has been instantiated or the + * {@link VoiceInteractionService} hosting this detector has been shut down. + */ + public Intent createIntentToReEnroll() { + if (DBG) Slog.d(TAG, "createIntentToReEnroll"); + synchronized (mLock) { + return getManageIntentLocked(MANAGE_ACTION_RE_ENROLL); } } @@ -462,12 +521,6 @@ public class AlwaysOnHotwordDetector { "Managing the given keyphrase is not supported"); } - if (action != MANAGE_ACTION_ENROLL - && action != MANAGE_ACTION_RE_ENROLL - && action != MANAGE_ACTION_UN_ENROLL) { - throw new IllegalArgumentException("Invalid action specified " + action); - } - return mKeyphraseEnrollmentInfo.getManageKeyphraseIntent(action, mText, mLocale); } diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index aecf488..e82057c 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -262,6 +262,8 @@ public class StaticLayout extends Layout { int fit = paraStart; float fitWidth = w; int fitAscent = 0, fitDescent = 0, fitTop = 0, fitBottom = 0; + // same as fitWidth but not including any trailing whitespace + float fitWidthGraphing = w; boolean hasTabOrEmoji = false; boolean hasTab = false; @@ -346,6 +348,9 @@ public class StaticLayout extends Layout { if (w <= width || isSpaceOrTab) { fitWidth = w; + if (!isSpaceOrTab) { + fitWidthGraphing = w; + } fit = j + 1; if (fmTop < fitTop) @@ -365,7 +370,7 @@ public class StaticLayout extends Layout { breakOpp[breakOppIndex] == j - paraStart + 1; if (isLineBreak) { - okWidth = w; + okWidth = fitWidthGraphing; ok = j + 1; if (fitTop < okTop) @@ -426,6 +431,7 @@ public class StaticLayout extends Layout { j = here - 1; // restart j-span loop from here, compensating for the j++ ok = fit = here; w = 0; + fitWidthGraphing = w; fitAscent = fitDescent = fitTop = fitBottom = 0; okAscent = okDescent = okTop = okBottom = 0; @@ -842,7 +848,7 @@ public class StaticLayout extends Layout { void prepare() { mMeasured = MeasuredText.obtain(); } - + void finish() { mMeasured = MeasuredText.recycle(mMeasured); } diff --git a/core/java/android/text/format/TimeFormatter.java b/core/java/android/text/format/TimeFormatter.java index ec79b36..3a63805 100644 --- a/core/java/android/text/format/TimeFormatter.java +++ b/core/java/android/text/format/TimeFormatter.java @@ -22,8 +22,7 @@ package android.text.format; import android.content.res.Resources; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; +import java.nio.CharBuffer; import java.util.Formatter; import java.util.Locale; import java.util.TimeZone; @@ -31,15 +30,13 @@ import libcore.icu.LocaleData; import libcore.util.ZoneInfo; /** - * Formatting logic for {@link Time}. Contains a port of Bionic's broken strftime_tz to Java. The - * main issue with this implementation is the treatment of characters as ASCII, despite returning - * localized (UTF-16) strings from the LocaleData. + * Formatting logic for {@link Time}. Contains a port of Bionic's broken strftime_tz to Java. * * <p>This class is not thread safe. */ class TimeFormatter { - // An arbitrary value outside the range representable by a byte / ASCII character code. - private static final int FORCE_LOWER_CASE = 0x100; + // An arbitrary value outside the range representable by a char. + private static final int FORCE_LOWER_CASE = -1; private static final int SECSPERMIN = 60; private static final int MINSPERHOUR = 60; @@ -62,10 +59,9 @@ class TimeFormatter { private final String dateTimeFormat; private final String timeOnlyFormat; private final String dateOnlyFormat; - private final Locale locale; private StringBuilder outputBuilder; - private Formatter outputFormatter; + private Formatter numberFormatter; public TimeFormatter() { synchronized (TimeFormatter.class) { @@ -84,7 +80,6 @@ class TimeFormatter { this.dateTimeFormat = sDateTimeFormat; this.timeOnlyFormat = sTimeOnlyFormat; this.dateOnlyFormat = sDateOnlyFormat; - this.locale = locale; localeData = sLocaleData; } } @@ -97,19 +92,21 @@ class TimeFormatter { StringBuilder stringBuilder = new StringBuilder(); outputBuilder = stringBuilder; - outputFormatter = new Formatter(stringBuilder, locale); + // This uses the US locale because number localization is handled separately (see below) + // and locale sensitive strings are output directly using outputBuilder. + numberFormatter = new Formatter(stringBuilder, Locale.US); formatInternal(pattern, wallTime, zoneInfo); String result = stringBuilder.toString(); // This behavior is the source of a bug since some formats are defined as being - // in ASCII. Generally localization is very broken. + // in ASCII and not localized. if (localeData.zeroDigit != '0') { result = localizeDigits(result); } return result; } finally { outputBuilder = null; - outputFormatter = null; + numberFormatter = null; } } @@ -132,38 +129,30 @@ class TimeFormatter { * {@link #outputBuilder}. */ private void formatInternal(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) { - // Convert to ASCII bytes to be compatible with old implementation behavior. - byte[] bytes = pattern.getBytes(StandardCharsets.US_ASCII); - if (bytes.length == 0) { - return; - } - - ByteBuffer formatBuffer = ByteBuffer.wrap(bytes); + CharBuffer formatBuffer = CharBuffer.wrap(pattern); while (formatBuffer.remaining() > 0) { - boolean outputCurrentByte = true; - char currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position())); - if (currentByteAsChar == '%') { - outputCurrentByte = handleToken(formatBuffer, wallTime, zoneInfo); + boolean outputCurrentChar = true; + char currentChar = formatBuffer.get(formatBuffer.position()); + if (currentChar == '%') { + outputCurrentChar = handleToken(formatBuffer, wallTime, zoneInfo); } - if (outputCurrentByte) { - currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position())); - outputBuilder.append(currentByteAsChar); + if (outputCurrentChar) { + outputBuilder.append(formatBuffer.get(formatBuffer.position())); } - formatBuffer.position(formatBuffer.position() + 1); } } - private boolean handleToken(ByteBuffer formatBuffer, ZoneInfo.WallTime wallTime, + private boolean handleToken(CharBuffer formatBuffer, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) { - // The byte at formatBuffer.position() is expected to be '%' at this point. + // The char at formatBuffer.position() is expected to be '%' at this point. int modifier = 0; while (formatBuffer.remaining() > 1) { - // Increment the position then get the new current byte. + // Increment the position then get the new current char. formatBuffer.position(formatBuffer.position() + 1); - char currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position())); - switch (currentByteAsChar) { + char currentChar = formatBuffer.get(formatBuffer.position()); + switch (currentChar) { case 'A': modifyAndAppend((wallTime.getWeekDay() < 0 || wallTime.getWeekDay() >= DAYSPERWEEK) @@ -206,7 +195,7 @@ class TimeFormatter { formatInternal("%m/%d/%y", wallTime, zoneInfo); return false; case 'd': - outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), + numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), wallTime.getMonthDay()); return false; case 'E': @@ -218,46 +207,46 @@ class TimeFormatter { case '0': case '^': case '#': - modifier = currentByteAsChar; + modifier = currentChar; continue; case 'e': - outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"), + numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"), wallTime.getMonthDay()); return false; case 'F': formatInternal("%Y-%m-%d", wallTime, zoneInfo); return false; case 'H': - outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), + numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), wallTime.getHour()); return false; case 'I': int hour = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12; - outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), hour); + numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), hour); return false; case 'j': int yearDay = wallTime.getYearDay() + 1; - outputFormatter.format(getFormat(modifier, "%03d", "%3d", "%d", "%03d"), + numberFormatter.format(getFormat(modifier, "%03d", "%3d", "%d", "%03d"), yearDay); return false; case 'k': - outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"), + numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"), wallTime.getHour()); return false; case 'l': int n2 = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12; - outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"), n2); + numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"), n2); return false; case 'M': - outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), + numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), wallTime.getMinute()); return false; case 'm': - outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), + numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), wallTime.getMonth() + 1); return false; case 'n': - modifyAndAppend("\n", modifier); + outputBuilder.append('\n'); return false; case 'p': modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1] @@ -274,27 +263,27 @@ class TimeFormatter { formatInternal("%I:%M:%S %p", wallTime, zoneInfo); return false; case 'S': - outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), + numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), wallTime.getSecond()); return false; case 's': int timeInSeconds = wallTime.mktime(zoneInfo); - modifyAndAppend(Integer.toString(timeInSeconds), modifier); + outputBuilder.append(Integer.toString(timeInSeconds)); return false; case 'T': formatInternal("%H:%M:%S", wallTime, zoneInfo); return false; case 't': - modifyAndAppend("\t", modifier); + outputBuilder.append('\t'); return false; case 'U': - outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), + numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), (wallTime.getYearDay() + DAYSPERWEEK - wallTime.getWeekDay()) / DAYSPERWEEK); return false; case 'u': int day = (wallTime.getWeekDay() == 0) ? DAYSPERWEEK : wallTime.getWeekDay(); - outputFormatter.format("%d", day); + numberFormatter.format("%d", day); return false; case 'V': /* ISO 8601 week number */ case 'G': /* ISO 8601 year (four digits) */ @@ -326,9 +315,9 @@ class TimeFormatter { --year; yday += isLeap(year) ? DAYSPERLYEAR : DAYSPERNYEAR; } - if (currentByteAsChar == 'V') { - outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), w); - } else if (currentByteAsChar == 'g') { + if (currentChar == 'V') { + numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), w); + } else if (currentChar == 'g') { outputYear(year, false, true, modifier); } else { outputYear(year, true, true, modifier); @@ -342,10 +331,10 @@ class TimeFormatter { int n = (wallTime.getYearDay() + DAYSPERWEEK - ( wallTime.getWeekDay() != 0 ? (wallTime.getWeekDay() - 1) : (DAYSPERWEEK - 1))) / DAYSPERWEEK; - outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n); + numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n); return false; case 'w': - outputFormatter.format("%d", wallTime.getWeekDay()); + numberFormatter.format("%d", wallTime.getWeekDay()); return false; case 'X': formatInternal(timeOnlyFormat, wallTime, zoneInfo); @@ -371,17 +360,17 @@ class TimeFormatter { return false; } int diff = wallTime.getGmtOffset(); - String sign; + char sign; if (diff < 0) { - sign = "-"; + sign = '-'; diff = -diff; } else { - sign = "+"; + sign = '+'; } - modifyAndAppend(sign, modifier); + outputBuilder.append(sign); diff /= SECSPERMIN; diff = (diff / MINSPERHOUR) * 100 + (diff % MINSPERHOUR); - outputFormatter.format(getFormat(modifier, "%04d", "%4d", "%d", "%04d"), diff); + numberFormatter.format(getFormat(modifier, "%04d", "%4d", "%d", "%04d"), diff); return false; } case '+': @@ -422,7 +411,6 @@ class TimeFormatter { break; default: outputBuilder.append(str); - } } @@ -443,14 +431,14 @@ class TimeFormatter { } if (outputTop) { if (lead == 0 && trail < 0) { - modifyAndAppend("-0", modifier); + outputBuilder.append("-0"); } else { - outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), lead); + numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), lead); } } if (outputBottom) { int n = ((trail < 0) ? -trail : trail); - outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n); + numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n); } } @@ -472,24 +460,24 @@ class TimeFormatter { } /** - * A broken implementation of {@link Character#isUpperCase(char)} that assumes ASCII in order to - * be compatible with the old native implementation. + * A broken implementation of {@link Character#isUpperCase(char)} that assumes ASCII codes in + * order to be compatible with the old native implementation. */ private static boolean brokenIsUpper(char toCheck) { return toCheck >= 'A' && toCheck <= 'Z'; } /** - * A broken implementation of {@link Character#isLowerCase(char)} that assumes ASCII in order to - * be compatible with the old native implementation. + * A broken implementation of {@link Character#isLowerCase(char)} that assumes ASCII codes in + * order to be compatible with the old native implementation. */ private static boolean brokenIsLower(char toCheck) { return toCheck >= 'a' && toCheck <= 'z'; } /** - * A broken implementation of {@link Character#toLowerCase(char)} that assumes ASCII in order to - * be compatible with the old native implementation. + * A broken implementation of {@link Character#toLowerCase(char)} that assumes ASCII codes in + * order to be compatible with the old native implementation. */ private static char brokenToLower(char input) { if (input >= 'A' && input <= 'Z') { @@ -499,8 +487,8 @@ class TimeFormatter { } /** - * A broken implementation of {@link Character#toUpperCase(char)} that assumes ASCII in order to - * be compatible with the old native implementation. + * A broken implementation of {@link Character#toUpperCase(char)} that assumes ASCII codes in + * order to be compatible with the old native implementation. */ private static char brokenToUpper(char input) { if (input >= 'a' && input <= 'z') { @@ -509,11 +497,4 @@ class TimeFormatter { return input; } - /** - * Safely convert a byte containing an ASCII character to a char, even for character codes - * > 127. - */ - private static char convertToChar(byte b) { - return (char) (b & 0xFF); - } } diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index 59ba71f..bd52e71 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -1656,7 +1656,7 @@ public abstract class Transition implements Cloneable { WindowId windowId = sceneRoot.getWindowId(); for (int i = numOldAnims - 1; i >= 0; i--) { AnimationInfo info = runningAnimators.valueAt(i); - if (info.view != null && windowId.equals(info.windowId)) { + if (info.view != null && windowId != null && windowId.equals(info.windowId)) { Animator anim = runningAnimators.keyAt(i); anim.pause(); } @@ -1689,7 +1689,7 @@ public abstract class Transition implements Cloneable { WindowId windowId = sceneRoot.getWindowId(); for (int i = numOldAnims - 1; i >= 0; i--) { AnimationInfo info = runningAnimators.valueAt(i); - if (info.view != null && windowId.equals(info.windowId)) { + if (info.view != null && windowId != null && windowId.equals(info.windowId)) { Animator anim = runningAnimators.keyAt(i); anim.resume(); } diff --git a/core/java/android/util/Size.java b/core/java/android/util/Size.java index d58f778..6424344 100644 --- a/core/java/android/util/Size.java +++ b/core/java/android/util/Size.java @@ -16,12 +16,15 @@ package android.util; -import static com.android.internal.util.Preconditions.*; +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.os.Parcel; +import android.os.Parcelable; /** * Immutable class for describing width and height dimensions in pixels. */ -public final class Size { +public final class Size implements Parcelable { /** * Create a new immutable Size instance. * @@ -33,6 +36,11 @@ public final class Size { mHeight = height; } + private Size(Parcel in) { + mWidth = in.readInt(); + mHeight = in.readInt(); + } + /** * Get the width of the size (in pixels). * @return width @@ -147,6 +155,29 @@ public final class Size { return mHeight ^ ((mWidth << (Integer.SIZE / 2)) | (mWidth >>> (Integer.SIZE / 2))); } + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mWidth); + out.writeInt(mHeight); + } + + public static final Parcelable.Creator<Size> CREATOR = new Parcelable.Creator<Size>() { + @Override + public Size createFromParcel(Parcel in) { + return new Size(in); + } + + @Override + public Size[] newArray(int size) { + return new Size[size]; + } + }; + private final int mWidth; private final int mHeight; -}; +} diff --git a/core/java/android/util/SizeF.java b/core/java/android/util/SizeF.java index 0a8b4ed..88bb439 100644 --- a/core/java/android/util/SizeF.java +++ b/core/java/android/util/SizeF.java @@ -16,7 +16,10 @@ package android.util; -import static com.android.internal.util.Preconditions.*; +import static com.android.internal.util.Preconditions.checkArgumentFinite; + +import android.os.Parcel; +import android.os.Parcelable; /** * Immutable class for describing width and height dimensions in some arbitrary @@ -25,7 +28,7 @@ import static com.android.internal.util.Preconditions.*; * Width and height are finite values stored as a floating point representation. * </p> */ -public final class SizeF { +public final class SizeF implements Parcelable { /** * Create a new immutable SizeF instance. * @@ -43,6 +46,11 @@ public final class SizeF { mHeight = checkArgumentFinite(height, "height"); } + private SizeF(Parcel in) { + mWidth = in.readFloat(); + mHeight = in.readFloat(); + } + /** * Get the width of the size (as an arbitrary unit). * @return width @@ -103,6 +111,29 @@ public final class SizeF { return Float.floatToIntBits(mWidth) ^ Float.floatToIntBits(mHeight); } + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeFloat(mWidth); + out.writeFloat(mHeight); + } + + public static final Parcelable.Creator<SizeF> CREATOR = new Parcelable.Creator<SizeF>() { + @Override + public SizeF createFromParcel(Parcel in) { + return new SizeF(in); + } + + @Override + public SizeF[] newArray(int size) { + return new SizeF[size]; + } + }; + private final float mWidth; private final float mHeight; -}; +} diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index de90899..edb3798 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -485,4 +485,6 @@ public abstract class HardwareRenderer { * Called by {@link ViewRootImpl} when a new performTraverals is scheduled. */ abstract void notifyFramePending(); + + abstract void registerAnimatingRenderNode(RenderNode animator); } diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index 099f153..9dc9766 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -167,10 +167,13 @@ public class RenderNode { public static final int STATUS_DREW = 0x4; private boolean mValid; - private final long mNativeRenderNode; + // Do not access directly unless you are ThreadedRenderer + final long mNativeRenderNode; + private final View mOwningView; - private RenderNode(String name) { + private RenderNode(String name, View owningView) { mNativeRenderNode = nCreate(name); + mOwningView = owningView; } /** @@ -178,6 +181,7 @@ public class RenderNode { */ private RenderNode(long nativePtr) { mNativeRenderNode = nativePtr; + mOwningView = null; } /** @@ -188,8 +192,8 @@ public class RenderNode { * * @return A new RenderNode. */ - public static RenderNode create(String name) { - return new RenderNode(name); + public static RenderNode create(String name, @Nullable View owningView) { + return new RenderNode(name, owningView); } /** @@ -805,7 +809,15 @@ public class RenderNode { /////////////////////////////////////////////////////////////////////////// public void addAnimator(RenderNodeAnimator animator) { + if (mOwningView == null || mOwningView.mAttachInfo == null) { + throw new IllegalStateException("Cannot start this animator on a detached view!"); + } nAddAnimator(mNativeRenderNode, animator.getNativeAnimator()); + mOwningView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(this); + } + + public void endAllAnimators() { + nEndAllAnimators(mNativeRenderNode); } /////////////////////////////////////////////////////////////////////////// @@ -891,6 +903,7 @@ public class RenderNode { /////////////////////////////////////////////////////////////////////////// private static native void nAddAnimator(long renderNode, long animatorPtr); + private static native void nEndAllAnimators(long renderNode); /////////////////////////////////////////////////////////////////////////// // Finalization diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index 9433237..fa4a13a 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -19,7 +19,6 @@ package android.view; import android.animation.Animator; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; -import android.animation.Animator.AnimatorListener; import android.graphics.Canvas; import android.graphics.CanvasProperty; import android.graphics.Paint; @@ -30,7 +29,6 @@ import com.android.internal.view.animation.FallbackLUTInterpolator; import com.android.internal.view.animation.HasNativeInterpolator; import com.android.internal.view.animation.NativeInterpolatorFactory; -import java.lang.ref.WeakReference; import java.util.ArrayList; /** @@ -89,8 +87,11 @@ public class RenderNodeAnimator extends Animator { private float mFinalValue; private TimeInterpolator mInterpolator; - private boolean mStarted = false; - private boolean mFinished = false; + private static final int STATE_PREPARE = 0; + private static final int STATE_DELAYED = 1; + private static final int STATE_RUNNING = 2; + private static final int STATE_FINISHED = 3; + private int mState = STATE_PREPARE; private long mUnscaledDuration = 300; private long mUnscaledStartDelay = 0; @@ -111,13 +112,11 @@ public class RenderNodeAnimator extends Animator { mRenderProperty = property; mFinalValue = finalValue; mUiThreadHandlesDelay = true; - init(nCreateAnimator(new WeakReference<RenderNodeAnimator>(this), - property, finalValue)); + init(nCreateAnimator(property, finalValue)); } public RenderNodeAnimator(CanvasProperty<Float> property, float finalValue) { init(nCreateCanvasPropertyFloatAnimator( - new WeakReference<RenderNodeAnimator>(this), property.getNativeContainer(), finalValue)); mUiThreadHandlesDelay = false; } @@ -132,14 +131,12 @@ public class RenderNodeAnimator extends Animator { */ public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, float finalValue) { init(nCreateCanvasPropertyPaintAnimator( - new WeakReference<RenderNodeAnimator>(this), property.getNativeContainer(), paintField, finalValue)); mUiThreadHandlesDelay = false; } public RenderNodeAnimator(int x, int y, float startRadius, float endRadius) { - init(nCreateRevealAnimator(new WeakReference<RenderNodeAnimator>(this), - x, y, startRadius, endRadius)); + init(nCreateRevealAnimator(x, y, startRadius, endRadius)); mUiThreadHandlesDelay = true; } @@ -148,7 +145,7 @@ public class RenderNodeAnimator extends Animator { } private void checkMutable() { - if (mStarted) { + if (mState != STATE_PREPARE) { throw new IllegalStateException("Animator has already started, cannot change it now!"); } } @@ -176,11 +173,11 @@ public class RenderNodeAnimator extends Animator { throw new IllegalStateException("Missing target!"); } - if (mStarted) { + if (mState != STATE_PREPARE) { throw new IllegalStateException("Already started!"); } - mStarted = true; + mState = STATE_DELAYED; applyInterpolator(); if (mStartDelay <= 0 || !mUiThreadHandlesDelay) { @@ -192,7 +189,8 @@ public class RenderNodeAnimator extends Animator { } private void doStart() { - nStart(mNativePtr.get()); + mState = STATE_RUNNING; + nStart(mNativePtr.get(), this); // Alpha is a special snowflake that has the canonical value stored // in mTransformationInfo instead of in RenderNode, so we need to update @@ -203,11 +201,7 @@ public class RenderNodeAnimator extends Animator { mViewTarget.mTransformationInfo.mAlpha = mFinalValue; } - final ArrayList<AnimatorListener> listeners = cloneListeners(); - final int numListeners = listeners == null ? 0 : listeners.size(); - for (int i = 0; i < numListeners; i++) { - listeners.get(i).onAnimationStart(this); - } + notifyStartListeners(); if (mViewTarget != null) { // Kick off a frame to start the process @@ -215,10 +209,21 @@ public class RenderNodeAnimator extends Animator { } } + private void notifyStartListeners() { + final ArrayList<AnimatorListener> listeners = cloneListeners(); + final int numListeners = listeners == null ? 0 : listeners.size(); + for (int i = 0; i < numListeners; i++) { + listeners.get(i).onAnimationStart(this); + } + } + @Override public void cancel() { - if (!mFinished) { - getHelper().removeDelayedAnimation(this); + if (mState != STATE_FINISHED) { + if (mState == STATE_DELAYED) { + getHelper().removeDelayedAnimation(this); + notifyStartListeners(); + } nEnd(mNativePtr.get()); final ArrayList<AnimatorListener> listeners = cloneListeners(); @@ -226,12 +231,17 @@ public class RenderNodeAnimator extends Animator { for (int i = 0; i < numListeners; i++) { listeners.get(i).onAnimationCancel(this); } + + if (mViewTarget != null) { + // Kick off a frame to flush the state change + mViewTarget.invalidateViewProperty(true, false); + } } } @Override public void end() { - if (!mFinished) { + if (mState != STATE_FINISHED) { nEnd(mNativePtr.get()); } } @@ -248,24 +258,21 @@ public class RenderNodeAnimator extends Animator { public void setTarget(View view) { mViewTarget = view; - mTarget = view.mRenderNode; - mTarget.addAnimator(this); + setTarget(mViewTarget.mRenderNode); } public void setTarget(Canvas canvas) { if (!(canvas instanceof GLES20RecordingCanvas)) { throw new IllegalArgumentException("Not a GLES20RecordingCanvas"); } - final GLES20RecordingCanvas recordingCanvas = (GLES20RecordingCanvas) canvas; setTarget(recordingCanvas.mNode); } - public void setTarget(RenderNode node) { + private void setTarget(RenderNode node) { if (mTarget != null) { throw new IllegalStateException("Target already set!"); } - mViewTarget = null; mTarget = node; mTarget.addAnimator(this); } @@ -308,12 +315,12 @@ public class RenderNodeAnimator extends Animator { @Override public boolean isRunning() { - return mStarted && !mFinished; + return mState == STATE_DELAYED || mState == STATE_RUNNING; } @Override public boolean isStarted() { - return mStarted; + return mState != STATE_PREPARE; } @Override @@ -328,13 +335,23 @@ public class RenderNodeAnimator extends Animator { } protected void onFinished() { - mFinished = true; + if (mState == STATE_DELAYED) { + getHelper().removeDelayedAnimation(this); + notifyStartListeners(); + } + mState = STATE_FINISHED; final ArrayList<AnimatorListener> listeners = cloneListeners(); final int numListeners = listeners == null ? 0 : listeners.size(); for (int i = 0; i < numListeners; i++) { listeners.get(i).onAnimationEnd(this); } + + // Release the native object, as it has a global reference to us. This + // breaks the cyclic reference chain, and allows this object to be + // GC'd + mNativePtr.release(); + mNativePtr = null; } @SuppressWarnings("unchecked") @@ -427,11 +444,8 @@ public class RenderNodeAnimator extends Animator { } // Called by native - private static void callOnFinished(WeakReference<RenderNodeAnimator> weakThis) { - RenderNodeAnimator animator = weakThis.get(); - if (animator != null) { - animator.onFinished(); - } + private static void callOnFinished(RenderNodeAnimator animator) { + animator.onFinished(); } @Override @@ -439,22 +453,20 @@ public class RenderNodeAnimator extends Animator { throw new IllegalStateException("Cannot clone this animator"); } - private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis, - int property, float finalValue); - private static native long nCreateCanvasPropertyFloatAnimator(WeakReference<RenderNodeAnimator> weakThis, + private static native long nCreateAnimator(int property, float finalValue); + private static native long nCreateCanvasPropertyFloatAnimator( long canvasProperty, float finalValue); - private static native long nCreateCanvasPropertyPaintAnimator(WeakReference<RenderNodeAnimator> weakThis, + private static native long nCreateCanvasPropertyPaintAnimator( long canvasProperty, int paintField, float finalValue); - private static native long nCreateRevealAnimator(WeakReference<RenderNodeAnimator> weakThis, + private static native long nCreateRevealAnimator( int x, int y, float startRadius, float endRadius); private static native void nSetStartValue(long nativePtr, float startValue); private static native void nSetDuration(long nativePtr, long duration); private static native long nGetDuration(long nativePtr); private static native void nSetStartDelay(long nativePtr, long startDelay); - private static native long nGetStartDelay(long nativePtr); private static native void nSetInterpolator(long animPtr, long interpolatorPtr); - private static native void nStart(long animPtr); + private static native void nStart(long animPtr, RenderNodeAnimator finishListener); private static native void nEnd(long animPtr); } diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 3af214d..ca08ecc 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -277,7 +277,11 @@ public class ThreadedRenderer extends HardwareRenderer { final int saveCount = canvas.save(); canvas.translate(mInsetLeft, mInsetTop); callbacks.onHardwarePreDraw(canvas); + + canvas.insertReorderBarrier(); canvas.drawRenderNode(view.getDisplayList()); + canvas.insertInorderBarrier(); + callbacks.onHardwarePostDraw(canvas); canvas.restoreToCount(saveCount); mRootNodeNeedsUpdate = false; @@ -312,6 +316,20 @@ public class ThreadedRenderer extends HardwareRenderer { attachInfo.mIgnoreDirtyState = false; + // register animating rendernodes which started animating prior to renderer + // creation, which is typical for animators started prior to first draw + if (attachInfo.mPendingAnimatingRenderNodes != null) { + final int count = attachInfo.mPendingAnimatingRenderNodes.size(); + for (int i = 0; i < count; i++) { + registerAnimatingRenderNode( + attachInfo.mPendingAnimatingRenderNodes.get(i)); + } + attachInfo.mPendingAnimatingRenderNodes.clear(); + // We don't need this anymore as subsequent calls to + // ViewRootImpl#attachRenderNodeAnimator will go directly to us. + attachInfo.mPendingAnimatingRenderNodes = null; + } + int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos, recordDuration, view.getResources().getDisplayMetrics().density); if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) { @@ -370,6 +388,11 @@ public class ThreadedRenderer extends HardwareRenderer { } @Override + void registerAnimatingRenderNode(RenderNode animator) { + nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode); + } + + @Override protected void finalize() throws Throwable { try { nDeleteProxy(mNativeProxy); @@ -466,6 +489,7 @@ public class ThreadedRenderer extends HardwareRenderer { private static native int nSyncAndDrawFrame(long nativeProxy, long frameTimeNanos, long recordDuration, float density); private static native void nDestroyCanvasAndSurface(long nativeProxy); + private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode); private static native void nInvokeFunctor(long functor, boolean waitForCompletion); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2d58ecf..fce6f0b 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3565,7 +3565,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS); mUserPaddingStart = UNDEFINED_PADDING; mUserPaddingEnd = UNDEFINED_PADDING; - mRenderNode = RenderNode.create(getClass().getName()); + mRenderNode = RenderNode.create(getClass().getName(), this); if (!sCompatibilityDone && context != null) { final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion; @@ -4161,7 +4161,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ View() { mResources = null; - mRenderNode = RenderNode.create(getClass().getName()); + mRenderNode = RenderNode.create(getClass().getName(), this); } private static SparseArray<String> getAttributeMap() { @@ -15183,9 +15183,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param renderNode Existing RenderNode, or {@code null} * @return A valid display list for the specified drawable */ - private static RenderNode getDrawableRenderNode(Drawable drawable, RenderNode renderNode) { + private RenderNode getDrawableRenderNode(Drawable drawable, RenderNode renderNode) { if (renderNode == null) { - renderNode = RenderNode.create(drawable.getClass().getName()); + renderNode = RenderNode.create(drawable.getClass().getName(), this); } final Rect bounds = drawable.getBounds(); @@ -19911,6 +19911,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, boolean mHardwareAccelerated; boolean mHardwareAccelerationRequested; HardwareRenderer mHardwareRenderer; + List<RenderNode> mPendingAnimatingRenderNodes; /** * The state of the display to which the window is attached, as reported @@ -20691,6 +20692,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mPosted) { return; } + final long timeSinceLastMillis = SystemClock.uptimeMillis() - mLastEventTimeMillis; final long minEventIntevalMillis = ViewConfiguration.getSendRecurringAccessibilityEventsInterval(); @@ -20699,7 +20701,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, run(); } else { postDelayed(this, minEventIntevalMillis - timeSinceLastMillis); - mPosted = true; mPostedWithDelay = true; } } diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java index bae0cfb..b73b9fa 100644 --- a/core/java/android/view/ViewPropertyAnimator.java +++ b/core/java/android/view/ViewPropertyAnimator.java @@ -430,6 +430,10 @@ public class ViewPropertyAnimator { } } mPendingAnimations.clear(); + mPendingSetupAction = null; + mPendingCleanupAction = null; + mPendingOnStartAction = null; + mPendingOnEndAction = null; mView.removeCallbacks(mAnimationStarter); if (mRTBackend != null) { mRTBackend.cancelAll(); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index bb469a3..49d925f 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -333,6 +333,8 @@ public final class ViewRootImpl implements ViewParent, private boolean mRemoved; private boolean mIsEmulator; + private boolean mIsCircularEmulator; + private final boolean mWindowIsRound; /** * Consistency verifier for debugging purposes. @@ -388,6 +390,8 @@ public final class ViewRootImpl implements ViewParent, mChoreographer = Choreographer.getInstance(); mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); loadSystemProperties(); + mWindowIsRound = context.getResources().getBoolean( + com.android.internal.R.bool.config_windowIsRound); } public static void addFirstDrawHandler(Runnable callback) { @@ -671,6 +675,17 @@ public final class ViewRootImpl implements ViewParent, ThreadedRenderer.invokeFunctor(functor, waitForCompletion); } + public void registerAnimatingRenderNode(RenderNode animator) { + if (mAttachInfo.mHardwareRenderer != null) { + mAttachInfo.mHardwareRenderer.registerAnimatingRenderNode(animator); + } else { + if (mAttachInfo.mPendingAnimatingRenderNodes == null) { + mAttachInfo.mPendingAnimatingRenderNodes = new ArrayList<RenderNode>(); + } + mAttachInfo.mPendingAnimatingRenderNodes.add(animator); + } + } + private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) { mAttachInfo.mHardwareAccelerated = false; mAttachInfo.mHardwareAccelerationRequested = false; @@ -1183,14 +1198,7 @@ public final class ViewRootImpl implements ViewParent, void dispatchApplyInsets(View host) { mDispatchContentInsets.set(mAttachInfo.mContentInsets); mDispatchStableInsets.set(mAttachInfo.mStableInsets); - boolean isRound = false; - if ((mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0 - && mDisplay.getDisplayId() == 0) { - // we're fullscreen and not hosted in an ActivityView - isRound = (mIsEmulator && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false)) - || mContext.getResources().getBoolean( - com.android.internal.R.bool.config_windowIsRound); - } + final boolean isRound = (mIsEmulator && mIsCircularEmulator) || mWindowIsRound; host.dispatchApplyWindowInsets(new WindowInsets( mDispatchContentInsets, null /* windowDecorInsets */, mDispatchStableInsets, isRound)); @@ -2329,6 +2337,16 @@ public final class ViewRootImpl implements ViewParent, Trace.traceEnd(Trace.TRACE_TAG_VIEW); } + // For whatever reason we didn't create a HardwareRenderer, end any + // hardware animations that are now dangling + if (mAttachInfo.mPendingAnimatingRenderNodes != null) { + final int count = mAttachInfo.mPendingAnimatingRenderNodes.size(); + for (int i = 0; i < count; i++) { + mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators(); + } + mAttachInfo.mPendingAnimatingRenderNodes.clear(); + } + if (mReportNextDraw) { mReportNextDraw = false; if (mAttachInfo.mHardwareRenderer != null) { @@ -5431,6 +5449,8 @@ public final class ViewRootImpl implements ViewParent, // detect emulator mIsEmulator = Build.HARDWARE.contains("goldfish"); + mIsCircularEmulator = + SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false); } }); } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 2c7ea3e..9b6f200 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -1304,12 +1304,22 @@ public abstract class Window { public abstract int getVolumeControlStream(); /** + * Sets a {@link MediaController} to send media keys and volume changes to. + * If set, this should be preferred for all media keys and volume requests + * sent to this window. + * + * @param controller The controller for the session which should receive + * media keys and volume changes. * @see android.app.Activity#setMediaController(android.media.session.MediaController) */ public void setMediaController(MediaController controller) { } /** + * Gets the {@link MediaController} that was previously set. + * + * @return The controller which should receive events. + * @see #setMediaController(android.media.session.MediaController) * @see android.app.Activity#getMediaController() */ public MediaController getMediaController() { diff --git a/core/java/android/webkit/PermissionRequest.java b/core/java/android/webkit/PermissionRequest.java index 862e8c2..6ad639c 100644 --- a/core/java/android/webkit/PermissionRequest.java +++ b/core/java/android/webkit/PermissionRequest.java @@ -19,8 +19,10 @@ package android.webkit; import android.net.Uri; /** - * This interface defines a permission request and is used when web content - * requests access to protected resources. + * This class defines a permission request and is used when web content + * requests access to protected resources. The permission request related events + * are delivered via {@link WebChromeClient#onPermissionRequest} and + * {@link WebChromeClient#onPermissionRequestCanceled}. * * Either {@link #grant(String[]) grant()} or {@link #deny()} must be called in UI * thread to respond to the request. diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index 35c9598..547acfa 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -452,14 +452,16 @@ public class WebChromeClient { */ public static abstract class FileChooserParams { /** Open single file. Requires that the file exists before allowing the user to pick it. */ - public static final int OPEN = 0; + public static final int MODE_OPEN = 0; /** Like Open but allows multiple files to be selected. */ - public static final int OPEN_MULTIPLE = 1; + public static final int MODE_OPEN_MULTIPLE = 1; /** Like Open but allows a folder to be selected. The implementation should enumerate - all files selected by this operation. */ - public static final int OPEN_FOLDER = 2; + all files selected by this operation. + This feature is not supported at the moment. + @hide */ + public static final int MODE_OPEN_FOLDER = 2; /** Allows picking a nonexistent file and saving it. */ - public static final int SAVE = 3; + public static final int MODE_SAVE = 3; /** * Returns a helper to simplify choosing and uploading files. The helper builds a default @@ -474,7 +476,8 @@ public class WebChromeClient { public abstract int getMode(); /** - * Returns an array of acceptable MIME types. The array will be empty if no + * Returns an array of acceptable MIME types. The returned MIME type + * could be partial such as audio/*. The array will be empty if no * acceptable types are specified. */ public abstract String[] getAcceptTypes(); @@ -494,9 +497,9 @@ public class WebChromeClient { public abstract CharSequence getTitle(); /** - * The file path of a default selection if specified, or null. + * The file name of a default selection if specified, or null. */ - public abstract String getDefaultFilename(); + public abstract String getFilenameHint(); }; /** diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 1b0cb3d..e1f19ee 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -1624,10 +1624,18 @@ public class WebView extends AbsoluteLayout } /** - * Enable drawing the entire HTML document at a significant performance - * cost. Call this to enable drawing and capturing HTML content outside of - * the WebView's viewport. This should be called before any WebViews are - * created. + * For apps targeting the L release, WebView has a new default behavior that reduces + * memory footprint and increases performance by intelligently choosing + * the portion of the HTML document that needs to be drawn. These + * optimizations are transparent to the developers. However, under certain + * circumstances, an App developer may want to disable them: + * 1. When an app uses {@link #onDraw} to do own drawing and accesses portions + * of the page that is way outside the visible portion of the page. + * 2. When an app uses {@link #capturePicture} to capture a very large HTML document. + * Note that capturePicture is a deprecated API. + * + * Enabling drawing the entire HTML document has a significant performance + * cost. This method should be called before any WebViews are created. */ public static void enableSlowWholeDocumentDraw() { getFactory().getStatics().enableSlowWholeDocumentDraw(); diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java index 7123b9a..ef8c006 100644 --- a/core/java/android/widget/ActionMenuPresenter.java +++ b/core/java/android/widget/ActionMenuPresenter.java @@ -19,6 +19,8 @@ package android.widget; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; import android.util.SparseBooleanArray; @@ -645,6 +647,23 @@ public class ActionMenuPresenter extends BaseMenuPresenter super.onInitializeAccessibilityNodeInfo(info); info.setCanOpenPopup(true); } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + // Set up the hotspot bounds to be centered on the image. + final Drawable d = getDrawable(); + final Drawable bg = getBackground(); + if (d != null && bg != null) { + final Rect bounds = d.getBounds(); + final int height = bottom - top; + final int offset = (height - bounds.width()) / 2; + final int hotspotLeft = bounds.left - offset; + final int hotspotRight = bounds.right + offset; + bg.setHotspotBounds(hotspotLeft, 0, hotspotRight, height); + } + } } private class OverflowPopup extends MenuPopupHelper { diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index eb232fd..3b16aba 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -35,9 +35,8 @@ import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; - import com.android.internal.R; - +import java.lang.ref.WeakReference; /** * <p>An editable text view that shows completion suggestions automatically @@ -85,8 +84,8 @@ import com.android.internal.R; * @attr ref android.R.styleable#AutoCompleteTextView_dropDownAnchor * @attr ref android.R.styleable#AutoCompleteTextView_dropDownWidth * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHeight - * @attr ref android.R.styleable#AutoCompleteTextView_dropDownVerticalOffset - * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHorizontalOffset + * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset + * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset */ public class AutoCompleteTextView extends EditText implements Filter.FilterListener { static final boolean DEBUG = false; @@ -130,7 +129,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } public AutoCompleteTextView(Context context, AttributeSet attrs) { - this(context, attrs, com.android.internal.R.attr.autoCompleteTextViewStyle); + this(context, attrs, R.attr.autoCompleteTextViewStyle); } public AutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) { @@ -141,23 +140,17 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - mPopup = new ListPopupWindow(context, attrs, - com.android.internal.R.attr.autoCompleteTextViewStyle); + mPopup = new ListPopupWindow(context, attrs, defStyleAttr, defStyleRes); mPopup.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); mPopup.setPromptPosition(ListPopupWindow.POSITION_PROMPT_BELOW); - final TypedArray a = context.obtainStyledAttributes(attrs, - com.android.internal.R.styleable.AutoCompleteTextView, defStyleAttr, defStyleRes); + final TypedArray a = context.obtainStyledAttributes( + attrs, R.styleable.AutoCompleteTextView, defStyleAttr, defStyleRes); - mThreshold = a.getInt( - R.styleable.AutoCompleteTextView_completionThreshold, 2); + mThreshold = a.getInt(R.styleable.AutoCompleteTextView_completionThreshold, 2); mPopup.setListSelector(a.getDrawable(R.styleable.AutoCompleteTextView_dropDownSelector)); - mPopup.setVerticalOffset((int) - a.getDimension(R.styleable.AutoCompleteTextView_dropDownVerticalOffset, 0.0f)); - mPopup.setHorizontalOffset((int) - a.getDimension(R.styleable.AutoCompleteTextView_dropDownHorizontalOffset, 0.0f)); - + // Get the anchor's id now, but the view won't be ready, so wait to actually get the // view and store it in mDropDownAnchorView lazily in getDropDownAnchorView later. // Defaults to NO_ID, in which case the getDropDownAnchorView method will simply return @@ -167,11 +160,9 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe // For dropdown width, the developer can specify a specific width, or MATCH_PARENT // (for full screen width) or WRAP_CONTENT (to match the width of the anchored view). - mPopup.setWidth(a.getLayoutDimension( - R.styleable.AutoCompleteTextView_dropDownWidth, + mPopup.setWidth(a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownWidth, ViewGroup.LayoutParams.WRAP_CONTENT)); - mPopup.setHeight(a.getLayoutDimension( - R.styleable.AutoCompleteTextView_dropDownHeight, + mPopup.setHeight(a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownHeight, ViewGroup.LayoutParams.WRAP_CONTENT)); mHintResource = a.getResourceId(R.styleable.AutoCompleteTextView_completionHintView, @@ -373,6 +364,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe * <p>Sets the vertical offset used for the auto-complete drop-down list.</p> * * @param offset the vertical offset + * + * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset */ public void setDropDownVerticalOffset(int offset) { mPopup.setVerticalOffset(offset); @@ -382,6 +375,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe * <p>Gets the vertical offset used for the auto-complete drop-down list.</p> * * @return the vertical offset + * + * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset */ public int getDropDownVerticalOffset() { return mPopup.getVerticalOffset(); @@ -391,6 +386,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe * <p>Sets the horizontal offset used for the auto-complete drop-down list.</p> * * @param offset the horizontal offset + * + * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset */ public void setDropDownHorizontalOffset(int offset) { mPopup.setHorizontalOffset(offset); @@ -400,6 +397,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe * <p>Gets the horizontal offset used for the auto-complete drop-down list.</p> * * @return the horizontal offset + * + * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset */ public int getDropDownHorizontalOffset() { return mPopup.getHorizontalOffset(); @@ -629,7 +628,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe */ public <T extends ListAdapter & Filterable> void setAdapter(T adapter) { if (mObserver == null) { - mObserver = new PopupDataSetObserver(); + mObserver = new PopupDataSetObserver(this); } else if (mAdapter != null) { mAdapter.unregisterDataSetObserver(mObserver); } @@ -1255,25 +1254,44 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } } - private class PopupDataSetObserver extends DataSetObserver { + /** + * Static inner listener that keeps a WeakReference to the actual AutoCompleteTextView. + * <p> + * This way, if adapter has a longer life span than the View, we won't leak the View, instead + * we will just leak a small Observer with 1 field. + */ + private static class PopupDataSetObserver extends DataSetObserver { + private final WeakReference<AutoCompleteTextView> mViewReference; + + private PopupDataSetObserver(AutoCompleteTextView view) { + mViewReference = new WeakReference<AutoCompleteTextView>(view); + } + @Override public void onChanged() { - if (mAdapter != null) { + final AutoCompleteTextView textView = mViewReference.get(); + if (textView != null && textView.mAdapter != null) { // If the popup is not showing already, showing it will cause // the list of data set observers attached to the adapter to // change. We can't do it from here, because we are in the middle // of iterating through the list of observers. - post(new Runnable() { - public void run() { - final ListAdapter adapter = mAdapter; - if (adapter != null) { - // This will re-layout, thus resetting mDataChanged, so that the - // listView click listener stays responsive - updateDropDownForFilter(adapter.getCount()); - } - } - }); + textView.post(updateRunnable); } } + + private final Runnable updateRunnable = new Runnable() { + @Override + public void run() { + final AutoCompleteTextView textView = mViewReference.get(); + if (textView == null) { + return; + } + final ListAdapter adapter = textView.mAdapter; + if (adapter == null) { + return; + } + textView.updateDropDownForFilter(adapter.getCount()); + } + }; } } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 29c8298..46b225d 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -147,7 +147,7 @@ public class Editor { boolean isDirty; public TextDisplayList(String name) { isDirty = true; - displayList = RenderNode.create(name); + displayList = RenderNode.create(name, null); } boolean needsRecord() { return isDirty || !displayList.isValid(); } } diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index 6a514ba..3c186e3 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -20,6 +20,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; +import android.content.res.TypedArray; import android.database.DataSetObserver; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -40,6 +41,7 @@ import android.view.ViewGroup; import android.view.ViewParent; import android.view.animation.AccelerateDecelerateInterpolator; +import com.android.internal.R; import com.android.internal.widget.AutoScrollHelper.AbsListViewAutoScroller; import java.util.Locale; @@ -208,6 +210,18 @@ public class ListPopupWindow { */ public ListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { mContext = context; + + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ListPopupWindow, + defStyleAttr, defStyleRes); + mDropDownHorizontalOffset = a.getDimensionPixelOffset( + R.styleable.ListPopupWindow_dropDownHorizontalOffset, 0); + mDropDownVerticalOffset = a.getDimensionPixelOffset( + R.styleable.ListPopupWindow_dropDownVerticalOffset, 0); + if (mDropDownVerticalOffset != 0) { + mDropDownVerticalOffsetSet = true; + } + a.recycle(); + mPopup = new PopupWindow(context, attrs, defStyleAttr, defStyleRes); mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); // Set the default layout direction to match the default locale one diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index 602f955..56bdb9b 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -176,7 +176,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback RemoteViewsAdapter adapter; final AppWidgetManager mgr = AppWidgetManager.getInstance(context); if ((adapter = mAdapter.get()) != null) { - mgr.unbindRemoteViewsService(context.getPackageName(), appWidgetId, intent); + mgr.unbindRemoteViewsService(context.getOpPackageName(), appWidgetId, intent); } else { Slog.w(TAG, "unbind: adapter was null"); } diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java index 9914800..98d52ff 100644 --- a/core/java/android/widget/Spinner.java +++ b/core/java/android/widget/Spinner.java @@ -49,14 +49,14 @@ import android.widget.PopupWindow.OnDismissListener; * * <p>See the <a href="{@docRoot}guide/topics/ui/controls/spinner.html">Spinners</a> guide.</p> * - * @attr ref android.R.styleable#Spinner_dropDownHorizontalOffset * @attr ref android.R.styleable#Spinner_dropDownSelector - * @attr ref android.R.styleable#Spinner_dropDownVerticalOffset * @attr ref android.R.styleable#Spinner_dropDownWidth * @attr ref android.R.styleable#Spinner_gravity * @attr ref android.R.styleable#Spinner_popupBackground * @attr ref android.R.styleable#Spinner_prompt * @attr ref android.R.styleable#Spinner_spinnerMode + * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset + * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset */ @Widget public class Spinner extends AbsSpinner implements OnClickListener { @@ -209,17 +209,6 @@ public class Spinner extends AbsSpinner implements OnClickListener { ViewGroup.LayoutParams.WRAP_CONTENT); popup.setBackgroundDrawable(a.getDrawable( com.android.internal.R.styleable.Spinner_popupBackground)); - final int verticalOffset = a.getDimensionPixelOffset( - com.android.internal.R.styleable.Spinner_dropDownVerticalOffset, 0); - if (verticalOffset != 0) { - popup.setVerticalOffset(verticalOffset); - } - - final int horizontalOffset = a.getDimensionPixelOffset( - com.android.internal.R.styleable.Spinner_dropDownHorizontalOffset, 0); - if (horizontalOffset != 0) { - popup.setHorizontalOffset(horizontalOffset); - } mPopup = popup; mForwardingListener = new ForwardingListener(this) { @@ -303,7 +292,7 @@ public class Spinner extends AbsSpinner implements OnClickListener { * * @param pixels Vertical offset in pixels * - * @attr ref android.R.styleable#Spinner_dropDownVerticalOffset + * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset */ public void setDropDownVerticalOffset(int pixels) { mPopup.setVerticalOffset(pixels); @@ -315,7 +304,7 @@ public class Spinner extends AbsSpinner implements OnClickListener { * * @return Vertical offset in pixels * - * @attr ref android.R.styleable#Spinner_dropDownVerticalOffset + * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset */ public int getDropDownVerticalOffset() { return mPopup.getVerticalOffset(); @@ -327,7 +316,7 @@ public class Spinner extends AbsSpinner implements OnClickListener { * * @param pixels Horizontal offset in pixels * - * @attr ref android.R.styleable#Spinner_dropDownHorizontalOffset + * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset */ public void setDropDownHorizontalOffset(int pixels) { mPopup.setHorizontalOffset(pixels); @@ -339,7 +328,7 @@ public class Spinner extends AbsSpinner implements OnClickListener { * * @return Horizontal offset in pixels * - * @attr ref android.R.styleable#Spinner_dropDownHorizontalOffset + * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset */ public int getDropDownHorizontalOffset() { return mPopup.getHorizontalOffset(); diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java index 818efaa..ece8aa4 100644 --- a/core/java/android/widget/Toolbar.java +++ b/core/java/android/widget/Toolbar.java @@ -244,6 +244,16 @@ public class Toolbar extends ViewGroup { // Set the default context, since setPopupTheme() may be a no-op. mPopupContext = mContext; setPopupTheme(a.getResourceId(R.styleable.Toolbar_popupTheme, 0)); + + final Drawable navIcon = a.getDrawable(R.styleable.Toolbar_navigationIcon); + if (navIcon != null) { + setNavigationIcon(navIcon); + final CharSequence navDesc = a.getText( + R.styleable.Toolbar_navigationContentDescription); + if (!TextUtils.isEmpty(navDesc)) { + setNavigationContentDescription(navDesc); + } + } a.recycle(); } @@ -669,6 +679,8 @@ public class Toolbar extends ViewGroup { * as screen readers or tooltips. * * @return The navigation button's content description + * + * @attr ref android.R.styleable#Toolbar_navigationContentDescription */ @Nullable public CharSequence getNavigationContentDescription() { @@ -682,6 +694,8 @@ public class Toolbar extends ViewGroup { * * @param resId Resource ID of a content description string to set, or 0 to * clear the description + * + * @attr ref android.R.styleable#Toolbar_navigationContentDescription */ public void setNavigationContentDescription(int resId) { setNavigationContentDescription(resId != 0 ? getContext().getText(resId) : null); @@ -694,6 +708,8 @@ public class Toolbar extends ViewGroup { * * @param description Content description to set, or <code>null</code> to * clear the content description + * + * @attr ref android.R.styleable#Toolbar_navigationContentDescription */ public void setNavigationContentDescription(@Nullable CharSequence description) { if (!TextUtils.isEmpty(description)) { @@ -715,6 +731,8 @@ public class Toolbar extends ViewGroup { * tooltips.</p> * * @param resId Resource ID of a drawable to set + * + * @attr ref android.R.styleable#Toolbar_navigationIcon */ public void setNavigationIcon(int resId) { setNavigationIcon(getContext().getDrawable(resId)); @@ -731,6 +749,8 @@ public class Toolbar extends ViewGroup { * tooltips.</p> * * @param icon Drawable to set, may be null to clear the icon + * + * @attr ref android.R.styleable#Toolbar_navigationIcon */ public void setNavigationIcon(@Nullable Drawable icon) { if (icon != null) { @@ -751,6 +771,8 @@ public class Toolbar extends ViewGroup { * Return the current drawable used as the navigation icon. * * @return The navigation icon drawable + * + * @attr ref android.R.styleable#Toolbar_navigationIcon */ @Nullable public Drawable getNavigationIcon() { @@ -1316,6 +1338,8 @@ public class Toolbar extends ViewGroup { final View bottomChild = layoutSubtitle ? mSubtitleTextView : mTitleTextView; final LayoutParams toplp = (LayoutParams) topChild.getLayoutParams(); final LayoutParams bottomlp = (LayoutParams) bottomChild.getLayoutParams(); + final boolean titleHasWidth = layoutTitle && mTitleTextView.getMeasuredWidth() > 0 + || layoutSubtitle && mSubtitleTextView.getMeasuredWidth() > 0; switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) { case Gravity.TOP: @@ -1343,7 +1367,7 @@ public class Toolbar extends ViewGroup { break; } if (isRtl) { - final int rd = mTitleMarginStart - collapsingMargins[1]; + final int rd = (titleHasWidth ? mTitleMarginStart : 0) - collapsingMargins[1]; right -= Math.max(0, rd); collapsingMargins[1] = Math.max(0, -rd); int titleRight = right; @@ -1366,9 +1390,11 @@ public class Toolbar extends ViewGroup { subtitleRight = subtitleRight - mTitleMarginEnd; titleTop = subtitleBottom + lp.bottomMargin; } - right = Math.min(titleRight, subtitleRight); + if (titleHasWidth) { + right = Math.min(titleRight, subtitleRight); + } } else { - final int ld = mTitleMarginStart - collapsingMargins[0]; + final int ld = (titleHasWidth ? mTitleMarginStart : 0) - collapsingMargins[0]; left += Math.max(0, ld); collapsingMargins[0] = Math.max(0, -ld); int titleLeft = left; @@ -1391,7 +1417,9 @@ public class Toolbar extends ViewGroup { subtitleLeft = subtitleRight + mTitleMarginEnd; titleTop = subtitleBottom + lp.bottomMargin; } - left = Math.max(titleLeft, subtitleLeft); + if (titleHasWidth) { + left = Math.max(titleLeft, subtitleLeft); + } } } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 61b4567..b6e7353 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -23,7 +23,6 @@ import android.app.usage.UsageStatsManager; import android.os.AsyncTask; import android.provider.Settings; import android.text.TextUtils; -import android.util.ArrayMap; import android.util.Slog; import android.widget.AbsListView; import android.widget.GridView; @@ -73,6 +72,7 @@ import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -100,7 +100,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic private boolean mResolvingHome = false; private UsageStatsManager mUsm; - private ArrayMap<String, UsageStats> mStats; + private Map<String, UsageStats> mStats; private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14; private boolean mRegistered; diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index ee406bd..eae4427 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -993,6 +993,6 @@ public final class BatteryStatsHelper { } catch (RemoteException e) { Log.w(TAG, "RemoteException:", e); } - return null; + return new BatteryStatsImpl(); } } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 299b0e6..69cdbff 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -2487,6 +2487,16 @@ public final class BatteryStatsImpl extends BatteryStats { addHistoryEventLocked(elapsedRealtime, uptime, code, name, uid); } + public void noteCurrentTimeChangedLocked() { + final long currentTime = System.currentTimeMillis(); + final long elapsedRealtime = SystemClock.elapsedRealtime(); + final long uptime = SystemClock.uptimeMillis(); + recordCurrentTimeChangeLocked(currentTime, elapsedRealtime, uptime); + if (isStartClockTimeValid()) { + mStartClockTime = currentTime; + } + } + public void noteProcessStartLocked(String name, int uid) { uid = mapUid(uid); if (isOnBattery()) { @@ -4060,7 +4070,20 @@ public final class BatteryStatsImpl extends BatteryStats { } } + boolean isStartClockTimeValid() { + return mStartClockTime > 365*24*60*60*1000L; + } + @Override public long getStartClockTime() { + if (!isStartClockTimeValid()) { + // If the last clock time we got was very small, then we hadn't had a real + // time yet, so try to get it again. + mStartClockTime = System.currentTimeMillis(); + if (isStartClockTimeValid()) { + recordCurrentTimeChangeLocked(mStartClockTime, SystemClock.elapsedRealtime(), + SystemClock.uptimeMillis()); + } + } return mStartClockTime; } @@ -6799,6 +6822,16 @@ public final class BatteryStatsImpl extends BatteryStats { } } + private void recordCurrentTimeChangeLocked(final long currentTime, final long elapsedRealtimeMs, + final long uptimeMs) { + if (mRecordingHistory) { + mHistoryCur.currentTime = currentTime; + addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME, + mHistoryCur); + mHistoryCur.currentTime = 0; + } + } + // This should probably be exposed in the API, though it's not critical private static final int BATTERY_PLUGGED_NONE = 0; @@ -8004,6 +8037,10 @@ public final class BatteryStatsImpl extends BatteryStats { public void writeSummaryToParcel(Parcel out, boolean inclHistory) { pullPendingStateUpdatesLocked(); + // Pull the clock time. This may update the time and make a new history entry + // if we had originally pulled a time before the RTC was set. + long startClockTime = getStartClockTime(); + final long NOW_SYS = SystemClock.uptimeMillis() * 1000; final long NOWREAL_SYS = SystemClock.elapsedRealtime() * 1000; @@ -8014,7 +8051,7 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(mStartCount); out.writeLong(computeUptime(NOW_SYS, STATS_SINCE_CHARGED)); out.writeLong(computeRealtime(NOWREAL_SYS, STATS_SINCE_CHARGED)); - out.writeLong(mStartClockTime); + out.writeLong(startClockTime); out.writeString(mStartPlatformVersion); out.writeString(mEndPlatformVersion); mOnBatteryTimeBase.writeSummaryToParcel(out, NOW_SYS, NOWREAL_SYS); @@ -8453,6 +8490,10 @@ public final class BatteryStatsImpl extends BatteryStats { // Need to update with current kernel wake lock counts. pullPendingStateUpdatesLocked(); + // Pull the clock time. This may update the time and make a new history entry + // if we had originally pulled a time before the RTC was set. + long startClockTime = getStartClockTime(); + final long uSecUptime = SystemClock.uptimeMillis() * 1000; final long uSecRealtime = SystemClock.elapsedRealtime() * 1000; final long batteryRealtime = mOnBatteryTimeBase.getRealtime(uSecRealtime); @@ -8463,7 +8504,7 @@ public final class BatteryStatsImpl extends BatteryStats { writeHistory(out, true, false); out.writeInt(mStartCount); - out.writeLong(mStartClockTime); + out.writeLong(startClockTime); out.writeString(mStartPlatformVersion); out.writeString(mEndPlatformVersion); out.writeLong(mUptime); @@ -8588,6 +8629,10 @@ public final class BatteryStatsImpl extends BatteryStats { public void prepareForDumpLocked() { // Need to retrieve current kernel wake lock stats before printing. pullPendingStateUpdatesLocked(); + + // Pull the clock time. This may update the time and make a new history entry + // if we had originally pulled a time before the RTC was set. + getStartClockTime(); } public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) { diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index 2967938..ba236f3 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -191,6 +191,20 @@ public class EditableInputConnection extends BaseInputConnection { public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) { if (DEBUG) Log.v(TAG, "requestUpdateCursorAnchorInfo " + cursorUpdateMode); + // It is possible that any other bit is used as a valid flag in a future release. + // We should reject the entire request in such a case. + final int KNOWN_FLAGS_MASK = InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE | + InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_MONITOR; + final int unknownFlags = cursorUpdateMode & ~KNOWN_FLAGS_MASK; + if (unknownFlags != 0) { + if (DEBUG) { + Log.d(TAG, "Rejecting requestUpdateCursorAnchorInfo due to unknown flags." + + " cursorUpdateMode=" + cursorUpdateMode + + " unknownFlags=" + unknownFlags); + } + return false; + } + if (mIMM == null) { // In this case, TYPE_CURSOR_ANCHOR_INFO is not handled. // TODO: Return some notification code rather than false to indicate method that |
