diff options
106 files changed, 2854 insertions, 697 deletions
| diff --git a/api/current.txt b/api/current.txt index 5dce913bcc18..300cb2d723db 100644 --- a/api/current.txt +++ b/api/current.txt @@ -44706,6 +44706,7 @@ package android.view.inputmethod {      method public java.lang.CharSequence getSelectedText(int);      method public java.lang.CharSequence getTextAfterCursor(int, int);      method public java.lang.CharSequence getTextBeforeCursor(int, int); +    method public boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);      method public boolean performContextMenuAction(int);      method public boolean performEditorAction(int);      method public boolean performPrivateCommand(java.lang.String, android.os.Bundle); @@ -44800,6 +44801,7 @@ package android.view.inputmethod {      field public static final int IME_NULL = 0; // 0x0      field public int actionId;      field public java.lang.CharSequence actionLabel; +    field public java.lang.String[] contentMimeTypes;      field public android.os.Bundle extras;      field public int fieldId;      field public java.lang.String fieldName; @@ -44871,6 +44873,7 @@ package android.view.inputmethod {      method public abstract java.lang.CharSequence getSelectedText(int);      method public abstract java.lang.CharSequence getTextAfterCursor(int, int);      method public abstract java.lang.CharSequence getTextBeforeCursor(int, int); +    method public abstract boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);      method public abstract boolean performContextMenuAction(int);      method public abstract boolean performEditorAction(int);      method public abstract boolean performPrivateCommand(java.lang.String, android.os.Bundle); @@ -44904,6 +44907,7 @@ package android.view.inputmethod {      method public java.lang.CharSequence getSelectedText(int);      method public java.lang.CharSequence getTextAfterCursor(int, int);      method public java.lang.CharSequence getTextBeforeCursor(int, int); +    method public boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);      method public boolean performContextMenuAction(int);      method public boolean performEditorAction(int);      method public boolean performPrivateCommand(java.lang.String, android.os.Bundle); @@ -44916,6 +44920,17 @@ package android.view.inputmethod {      method public void setTarget(android.view.inputmethod.InputConnection);    } +  public class InputContentInfo implements android.os.Parcelable { +    ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription); +    ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription, android.net.Uri); +    method public int describeContents(); +    method public android.net.Uri getContentUri(); +    method public android.content.ClipDescription getDescription(); +    method public android.net.Uri getLinkUri(); +    method public void writeToParcel(android.os.Parcel, int); +    field public static final android.os.Parcelable.Creator<android.view.inputmethod.InputContentInfo> CREATOR; +  } +    public abstract interface InputMethod {      method public abstract void attachToken(android.os.IBinder);      method public abstract void bindInput(android.view.inputmethod.InputBinding); diff --git a/api/system-current.txt b/api/system-current.txt index 1b826010af8b..6b72c51b3fbf 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -47709,6 +47709,7 @@ package android.view.inputmethod {      method public java.lang.CharSequence getSelectedText(int);      method public java.lang.CharSequence getTextAfterCursor(int, int);      method public java.lang.CharSequence getTextBeforeCursor(int, int); +    method public boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);      method public boolean performContextMenuAction(int);      method public boolean performEditorAction(int);      method public boolean performPrivateCommand(java.lang.String, android.os.Bundle); @@ -47803,6 +47804,7 @@ package android.view.inputmethod {      field public static final int IME_NULL = 0; // 0x0      field public int actionId;      field public java.lang.CharSequence actionLabel; +    field public java.lang.String[] contentMimeTypes;      field public android.os.Bundle extras;      field public int fieldId;      field public java.lang.String fieldName; @@ -47874,6 +47876,7 @@ package android.view.inputmethod {      method public abstract java.lang.CharSequence getSelectedText(int);      method public abstract java.lang.CharSequence getTextAfterCursor(int, int);      method public abstract java.lang.CharSequence getTextBeforeCursor(int, int); +    method public abstract boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);      method public abstract boolean performContextMenuAction(int);      method public abstract boolean performEditorAction(int);      method public abstract boolean performPrivateCommand(java.lang.String, android.os.Bundle); @@ -47907,6 +47910,7 @@ package android.view.inputmethod {      method public java.lang.CharSequence getSelectedText(int);      method public java.lang.CharSequence getTextAfterCursor(int, int);      method public java.lang.CharSequence getTextBeforeCursor(int, int); +    method public boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);      method public boolean performContextMenuAction(int);      method public boolean performEditorAction(int);      method public boolean performPrivateCommand(java.lang.String, android.os.Bundle); @@ -47919,6 +47923,17 @@ package android.view.inputmethod {      method public void setTarget(android.view.inputmethod.InputConnection);    } +  public class InputContentInfo implements android.os.Parcelable { +    ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription); +    ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription, android.net.Uri); +    method public int describeContents(); +    method public android.net.Uri getContentUri(); +    method public android.content.ClipDescription getDescription(); +    method public android.net.Uri getLinkUri(); +    method public void writeToParcel(android.os.Parcel, int); +    field public static final android.os.Parcelable.Creator<android.view.inputmethod.InputContentInfo> CREATOR; +  } +    public abstract interface InputMethod {      method public abstract void attachToken(android.os.IBinder);      method public abstract void bindInput(android.view.inputmethod.InputBinding); diff --git a/api/test-current.txt b/api/test-current.txt index 7a64c349809e..f0df28b1b992 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -44786,6 +44786,7 @@ package android.view.inputmethod {      method public java.lang.CharSequence getSelectedText(int);      method public java.lang.CharSequence getTextAfterCursor(int, int);      method public java.lang.CharSequence getTextBeforeCursor(int, int); +    method public boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);      method public boolean performContextMenuAction(int);      method public boolean performEditorAction(int);      method public boolean performPrivateCommand(java.lang.String, android.os.Bundle); @@ -44880,6 +44881,7 @@ package android.view.inputmethod {      field public static final int IME_NULL = 0; // 0x0      field public int actionId;      field public java.lang.CharSequence actionLabel; +    field public java.lang.String[] contentMimeTypes;      field public android.os.Bundle extras;      field public int fieldId;      field public java.lang.String fieldName; @@ -44951,6 +44953,7 @@ package android.view.inputmethod {      method public abstract java.lang.CharSequence getSelectedText(int);      method public abstract java.lang.CharSequence getTextAfterCursor(int, int);      method public abstract java.lang.CharSequence getTextBeforeCursor(int, int); +    method public abstract boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);      method public abstract boolean performContextMenuAction(int);      method public abstract boolean performEditorAction(int);      method public abstract boolean performPrivateCommand(java.lang.String, android.os.Bundle); @@ -44984,6 +44987,7 @@ package android.view.inputmethod {      method public java.lang.CharSequence getSelectedText(int);      method public java.lang.CharSequence getTextAfterCursor(int, int);      method public java.lang.CharSequence getTextBeforeCursor(int, int); +    method public boolean insertContent(android.view.inputmethod.InputContentInfo, android.os.Bundle);      method public boolean performContextMenuAction(int);      method public boolean performEditorAction(int);      method public boolean performPrivateCommand(java.lang.String, android.os.Bundle); @@ -44996,6 +45000,17 @@ package android.view.inputmethod {      method public void setTarget(android.view.inputmethod.InputConnection);    } +  public class InputContentInfo implements android.os.Parcelable { +    ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription); +    ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription, android.net.Uri); +    method public int describeContents(); +    method public android.net.Uri getContentUri(); +    method public android.content.ClipDescription getDescription(); +    method public android.net.Uri getLinkUri(); +    method public void writeToParcel(android.os.Parcel, int); +    field public static final android.os.Parcelable.Creator<android.view.inputmethod.InputContentInfo> CREATOR; +  } +    public abstract interface InputMethod {      method public abstract void attachToken(android.os.IBinder);      method public abstract void bindInput(android.view.inputmethod.InputBinding); diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index 4edf249ce065..0c7ee2c8c2fe 100644 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -602,7 +602,9 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio          long currentTime = AnimationUtils.currentAnimationTimeMillis();          mStartTime = currentTime - seekTime;          mStartTimeCommitted = true; // do not allow start time to be compensated for jank -        if (!mRunning) { +        if (!isPulsingInternal()) { +            // If the animation loop hasn't started, the startTime will be adjusted in the first +            // frame based on seek fraction.              mSeekFraction = fraction;          }          mOverallFraction = fraction; @@ -980,6 +982,10 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio          mStarted = true;          mPaused = false;          mRunning = false; +        // Resets mLastFrameTime when start() is called, so that if the animation was running, +        // calling start() would put the animation in the +        // started-but-not-yet-reached-the-first-frame phase. +        mLastFrameTime = 0;          AnimationHandler animationHandler = AnimationHandler.getInstance();          animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale)); @@ -1095,7 +1101,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio       */      @Override      public void reverse() { -        if (mRunning) { +        if (isPulsingInternal()) {              long currentTime = AnimationUtils.currentAnimationTimeMillis();              long currentPlayTime = currentTime - mStartTime;              long timeLeft = getScaledDuration() - currentPlayTime; @@ -1103,6 +1109,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio              mStartTimeCommitted = true; // do not allow start time to be compensated for jank              mReversing = !mReversing;          } else if (mStarted) { +            mReversing = !mReversing;              end();          } else {              start(true); @@ -1177,6 +1184,15 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio      }      /** +     * Internal only: This tracks whether the animation has gotten on the animation loop. Note +     * this is different than {@link #isRunning()} in that the latter tracks the time after start() +     * is called (or after start delay if any), which may be before the animation loop starts. +     */ +    private boolean isPulsingInternal() { +        return mLastFrameTime > 0; +    } + +    /**       * Returns the name of this animator for debugging purposes.       */      String getNameForTrace() { diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index e526c17dde9d..1e4ffbeca33d 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1340,9 +1340,14 @@ public class AppOpsManager {      /** @hide */      public void setUserRestriction(int code, boolean restricted, IBinder token,              String[] exceptionPackages) { +        setUserRestrictionForUser(code, restricted, token, exceptionPackages, mContext.getUserId()); +    } + +    /** @hide */ +    public void setUserRestrictionForUser(int code, boolean restricted, IBinder token, +            String[] exceptionPackages, int userId) {          try { -            mService.setUserRestriction(code, restricted, token, mContext.getUserId(), -                  exceptionPackages); +            mService.setUserRestriction(code, restricted, token, userId, exceptionPackages);          } catch (RemoteException e) {              throw e.rethrowFromSystemServer();          } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 83a20668b3b4..f896fb592be9 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1986,6 +1986,10 @@ public class Notification implements Parcelable                      new Throwable());          } +        if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) { +            extras.putBoolean(EXTRA_SHOW_WHEN, true); +        } +          // ensure that any information already set directly is preserved          final Notification.Builder builder = new Notification.Builder(context, this); @@ -2269,6 +2273,14 @@ public class Notification implements Parcelable                      Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));                  } +                if (mN.getSmallIcon() == null && mN.icon != 0) { +                    setSmallIcon(mN.icon); +                } + +                if (mN.getLargeIcon() == null && mN.largeIcon != null) { +                    setLargeIcon(mN.largeIcon); +                } +                  String templateClass = mN.extras.getString(EXTRA_TEMPLATE);                  if (!TextUtils.isEmpty(templateClass)) {                      final Class<? extends Style> styleClass @@ -3171,8 +3183,8 @@ public class Notification implements Parcelable          }          private void resetContentMargins(RemoteViews contentView) { -            contentView.setViewLayoutMarginEnd(R.id.line1, 0); -            contentView.setViewLayoutMarginEnd(R.id.text, 0); +            contentView.setViewLayoutMarginEndDimen(R.id.line1, 0); +            contentView.setViewLayoutMarginEndDimen(R.id.text, 0);          }          private RemoteViews applyStandardTemplate(int resId) { @@ -3266,11 +3278,10 @@ public class Notification implements Parcelable                  contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);                  contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);                  processLargeLegacyIcon(mN.mLargeIcon, contentView); -                int endMargin = mContext.getResources().getDimensionPixelSize( -                        R.dimen.notification_content_picture_margin); -                contentView.setViewLayoutMarginEnd(R.id.line1, endMargin); -                contentView.setViewLayoutMarginEnd(R.id.text, endMargin); -                contentView.setViewLayoutMarginEnd(R.id.progress, endMargin); +                int endMargin = R.dimen.notification_content_picture_margin; +                contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin); +                contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin); +                contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);              }          } @@ -3394,6 +3405,8 @@ public class Notification implements Parcelable              big.setTextViewText(R.id.notification_material_reply_text_2, null);              big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);              big.setTextViewText(R.id.notification_material_reply_text_3, null); + +            big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);          }          private RemoteViews applyStandardTemplateWithActions(int layoutId) { @@ -3416,6 +3429,8 @@ public class Notification implements Parcelable              if (N > 0) {                  big.setViewVisibility(R.id.actions_container, View.VISIBLE);                  big.setViewVisibility(R.id.actions, View.VISIBLE); +                big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, +                        R.dimen.notification_action_list_height);                  if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;                  for (int i=0; i<N; i++) {                      Action action = mActions.get(i); @@ -4570,12 +4585,21 @@ public class Notification implements Parcelable                      : mConversationTitle;              boolean hasTitle = !TextUtils.isEmpty(title); -            if (!hasTitle && mMessages.size() == 1) { -                CharSequence sender = mMessages.get(0).mSender; -                CharSequence text = mMessages.get(0).mText; +            if (mMessages.size() == 1) { +                // Special case for a single message: Use the big text style +                // so the collapsed and expanded versions match nicely. +                CharSequence bigTitle; +                CharSequence text; +                if (hasTitle) { +                    bigTitle = title; +                    text = makeMessageLine(mMessages.get(0)); +                } else { +                    bigTitle = mMessages.get(0).mSender; +                    text = mMessages.get(0).mText; +                }                  RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(                          mBuilder.getBigTextLayoutResource(), -                        false /* progress */, sender, null /* text */); +                        false /* progress */, bigTitle, null /* text */);                  BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);                  return contentView;              } @@ -4595,12 +4619,13 @@ public class Notification implements Parcelable              }              int i=0; -            int titlePadding = mBuilder.mContext.getResources().getDimensionPixelSize( -                    R.dimen.notification_messaging_spacing); -            contentView.setViewLayoutMarginBottom(R.id.line1, hasTitle ? titlePadding : 0); +            contentView.setViewLayoutMarginBottomDimen(R.id.line1, +                    hasTitle ? R.dimen.notification_messaging_spacing : 0);              contentView.setInt(R.id.notification_messaging, "setNumIndentLines",                      mBuilder.mN.mLargeIcon == null ? 0 : (hasTitle ? 1 : 2)); +            int contractedChildId = View.NO_ID; +            Message contractedMessage = findLatestIncomingMessage();              int firstMessage = Math.max(0, mMessages.size() - rowIds.length);              while (firstMessage + i < mMessages.size() && i < rowIds.length) {                  Message m = mMessages.get(firstMessage + i); @@ -4609,8 +4634,15 @@ public class Notification implements Parcelable                  contentView.setViewVisibility(rowId, View.VISIBLE);                  contentView.setTextViewText(rowId, makeMessageLine(m)); +                if (contractedMessage == m) { +                    contractedChildId = rowId; +                } +                  i++;              } +            // Record this here to allow transformation between the contracted and expanded views. +            contentView.setInt(R.id.notification_messaging, "setContractedChildId", +                    contractedChildId);              return contentView;          } @@ -4964,11 +4996,10 @@ public class Notification implements Parcelable                  final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);                  boolean hasProgress = max != 0 || ind;                  if (mBuilder.mN.mLargeIcon != null && !hasProgress) { -                    endMargin = mBuilder.mContext.getResources().getDimensionPixelSize( -                            R.dimen.notification_content_picture_margin); +                    endMargin = R.dimen.notification_content_picture_margin;                  }              } -            contentView.setViewLayoutMarginEnd(id, endMargin); +            contentView.setViewLayoutMarginEndDimen(id, endMargin);          }      } @@ -5153,13 +5184,11 @@ public class Notification implements Parcelable              }              handleImage(view);              // handle the content margin -            int endMargin = mBuilder.mContext.getResources().getDimensionPixelSize( -                    R.dimen.notification_content_margin_end);; +            int endMargin = R.dimen.notification_content_margin_end;              if (mBuilder.mN.mLargeIcon != null) { -                endMargin += mBuilder.mContext.getResources().getDimensionPixelSize( -                        R.dimen.notification_content_picture_margin); +                endMargin = R.dimen.notification_content_plus_picture_margin_end;              } -            view.setViewLayoutMarginEnd(R.id.notification_main_column, endMargin); +            view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);              return view;          } @@ -5190,8 +5219,8 @@ public class Notification implements Parcelable          private void handleImage(RemoteViews contentView) {              if (mBuilder.mN.mLargeIcon != null) { -                contentView.setViewLayoutMarginEnd(R.id.line1, 0); -                contentView.setViewLayoutMarginEnd(R.id.text, 0); +                contentView.setViewLayoutMarginEndDimen(R.id.line1, 0); +                contentView.setViewLayoutMarginEndDimen(R.id.text, 0);              }          } @@ -5306,13 +5335,11 @@ public class Notification implements Parcelable                  remoteViews.addView(R.id.notification_main_column, customContent);              }              // also update the end margin if there is an image -            int endMargin = mBuilder.mContext.getResources().getDimensionPixelSize( -                    R.dimen.notification_content_margin_end); +            int endMargin = R.dimen.notification_content_margin_end;              if (mBuilder.mN.mLargeIcon != null) { -                endMargin += mBuilder.mContext.getResources().getDimensionPixelSize( -                        R.dimen.notification_content_picture_margin); +                endMargin = R.dimen.notification_content_plus_picture_margin_end;              } -            remoteViews.setViewLayoutMarginEnd(R.id.notification_main_column, endMargin); +            remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);          }      } diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java index 54a2f7aff7d6..9a0cd564e35f 100644 --- a/core/java/android/app/admin/DevicePolicyManagerInternal.java +++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java @@ -17,7 +17,6 @@  package android.app.admin;  import android.content.Intent; -import android.os.UserHandle;  import java.util.List; @@ -48,6 +47,8 @@ public abstract class DevicePolicyManagerInternal {       * Gets the packages whose widget providers are white-listed to be       * available in the parent user.       * +     * <p>This takes the DPMS lock.  DO NOT call from PM/UM/AM with their lock held. +     *       * @param profileId The profile id.       * @return The list of packages if such or empty list if there are       *    no white-listed packages or the profile id is not a managed @@ -59,6 +60,8 @@ public abstract class DevicePolicyManagerInternal {       * Adds a listener for changes in the white-listed packages to show       * cross-profile app widgets.       * +     * <p>This takes the DPMS lock.  DO NOT call from PM/UM/AM with their lock held. +     *       * @param listener The listener to add.       */      public abstract void addOnCrossProfileWidgetProvidersChangeListener( @@ -67,6 +70,9 @@ public abstract class DevicePolicyManagerInternal {      /**       * Checks if an app with given uid is an active device admin of its user and has the policy       * specified. +     * +     * <p>This takes the DPMS lock.  DO NOT call from PM/UM/AM with their lock held. +     *       * @param uid App uid.       * @param reqPolicy Required policy, for policies see {@link DevicePolicyManager}.       * @return true if the uid is an active admin with the given policy. @@ -74,21 +80,12 @@ public abstract class DevicePolicyManagerInternal {      public abstract boolean isActiveAdminWithPolicy(int uid, int reqPolicy);      /** -     * Checks if a given package has a device or a profile owner for the given user. -     * <p> -     * <em>Note: does <b>not</b> support negative userIds like {@link UserHandle#USER_ALL}</em> -     * -     * @param packageName The package to check -     * @param userId the userId to check for. -     * @return true if package has a device or profile owner, false otherwise. -     */ -    public abstract boolean hasDeviceOwnerOrProfileOwner(String packageName, int userId); - -    /**       * Creates an intent to show the admin support dialog to let the user know that the package is       * suspended by the admin. This assumes that {@param packageName} is suspended by the       * device/profile owner. The caller should check if the package is suspended or not.       * +     * <p>This method does not take the DPMS lock.  Safe to be called from anywhere. +     *       * @param packageName The package that is suspended       * @param userId The user having the suspended package.       * @return The intent to trigger the admin support dialog. diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 13ebb823bd78..14f7727e61a0 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -18,6 +18,7 @@ package android.content.pm;  import android.content.ComponentName;  import android.content.pm.PackageManager.NameNotFoundException; +import android.util.SparseArray;  import java.util.List; @@ -147,4 +148,16 @@ public abstract class PackageManagerInternal {       */      public abstract ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,              int userId); + +    /** +     * Called by DeviceOwnerManagerService to set the package names of device owner and profile +     * owners. +     */ +    public abstract void setDeviceAndProfileOwnerPackages( +            int deviceOwnerUserId, String deviceOwner, SparseArray<String> profileOwners); + +    /** +     * Whether a package's data be cleared. +     */ +    public abstract boolean canPackageBeWiped(int userId, String packageName);  } diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index d5491d3a808a..7702c174ba2f 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -864,6 +864,34 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {                  super.close();              }          } + +        @Override +        public int read() throws IOException { +            final int result = super.read(); +            if (result == -1 && mPfd.canDetectErrors()) { +                // Check for errors only on EOF, to minimize overhead. +                mPfd.checkError(); +            } +            return result; +        } + +        @Override +        public int read(byte[] b) throws IOException { +            final int result = super.read(b); +            if (result == -1 && mPfd.canDetectErrors()) { +                mPfd.checkError(); +            } +            return result; +        } + +        @Override +        public int read(byte[] b, int off, int len) throws IOException { +            final int result = super.read(b, off, len); +            if (result == -1 && mPfd.canDetectErrors()) { +                mPfd.checkError(); +            } +            return result; +        }      }      /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 7318ff058945..402f69414a03 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6246,6 +6246,21 @@ public final class Settings {                  "automatic_storage_manager_enabled";          /** +         * How many days of information for the automatic storage manager to retain on the device. +         * +         * @hide +         */ +        public static final String AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN = +                "automatic_storage_manager_days_to_retain"; + +        /** +         * Default number of days of information for the automatic storage manager to retain. +         * +         * @hide +         */ +        public static final int AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_DEFAULT = 90; + +        /**           * This are the settings to be backed up.           *           * NOTE: Settings are backed up and restored in the order they appear diff --git a/core/java/android/security/net/config/SystemCertificateSource.java b/core/java/android/security/net/config/SystemCertificateSource.java index abef7b453c79..cfb195b9509c 100644 --- a/core/java/android/security/net/config/SystemCertificateSource.java +++ b/core/java/android/security/net/config/SystemCertificateSource.java @@ -25,7 +25,10 @@ import java.io.File;   * @hide   */  public final class SystemCertificateSource extends DirectoryCertificateSource { -    private static final SystemCertificateSource INSTANCE = new SystemCertificateSource(); +    private static class NoPreloadHolder { +        private static final SystemCertificateSource INSTANCE = new SystemCertificateSource(); +    } +      private final File mUserRemovedCaDir;      private SystemCertificateSource() { @@ -35,7 +38,7 @@ public final class SystemCertificateSource extends DirectoryCertificateSource {      }      public static SystemCertificateSource getInstance() { -        return INSTANCE; +        return NoPreloadHolder.INSTANCE;      }      @Override diff --git a/core/java/android/security/net/config/UserCertificateSource.java b/core/java/android/security/net/config/UserCertificateSource.java index 1a7d92456a3d..d6e2b3a8ca1f 100644 --- a/core/java/android/security/net/config/UserCertificateSource.java +++ b/core/java/android/security/net/config/UserCertificateSource.java @@ -25,7 +25,9 @@ import java.io.File;   * @hide   */  public final class UserCertificateSource extends DirectoryCertificateSource { -    private static final UserCertificateSource INSTANCE = new UserCertificateSource(); +    private static class NoPreloadHolder { +        private static final UserCertificateSource INSTANCE = new UserCertificateSource(); +    }      private UserCertificateSource() {          super(new File( @@ -33,7 +35,7 @@ public final class UserCertificateSource extends DirectoryCertificateSource {      }      public static UserCertificateSource getInstance() { -        return INSTANCE; +        return NoPreloadHolder.INSTANCE;      }      @Override diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ee23a99ffe7e..95c5c245862d 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -20538,6 +20538,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,          if (ViewDebug.DEBUG_DRAG) {              Log.d(VIEW_LOG_TAG, "startDragAndDrop: data=" + data + " flags=" + flags);          } +        if (mAttachInfo == null) { +            Log.w(VIEW_LOG_TAG, "startDragAndDrop called on a detached view."); +            return false; +        }          boolean okay = false;          Point shadowSize = new Point(); @@ -20614,6 +20618,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,          if (ViewDebug.DEBUG_DRAG) {              Log.d(VIEW_LOG_TAG, "cancelDragAndDrop");          } +        if (mAttachInfo == null) { +            Log.w(VIEW_LOG_TAG, "cancelDragAndDrop called on a detached view."); +            return; +        }          if (mAttachInfo.mDragToken != null) {              try {                  mAttachInfo.mSession.cancelDragAndDrop(mAttachInfo.mDragToken); @@ -20636,6 +20644,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,          if (ViewDebug.DEBUG_DRAG) {              Log.d(VIEW_LOG_TAG, "updateDragShadow");          } +        if (mAttachInfo == null) { +            Log.w(VIEW_LOG_TAG, "updateDragShadow called on a detached view."); +            return; +        }          if (mAttachInfo.mDragToken != null) {              try {                  Canvas canvas = mAttachInfo.mDragSurface.lockCanvas(null); diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 89dec2d22d7a..7c3fc8b5808b 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -851,4 +851,9 @@ public class BaseInputConnection implements InputConnection {          endBatchEdit();      } + +    /** +     * The default implementation does nothing. +     */ +    public boolean insertContent(InputContentInfo inputContentInfo, Bundle opts) { return false; }  } diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java index 7b7ccae62ded..7ed0d27f65ec 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -25,6 +25,8 @@ import android.text.InputType;  import android.text.TextUtils;  import android.util.Printer; +import java.util.Arrays; +  /**   * An EditorInfo describes several attributes of a text editing object   * that an input method is communicating with (typically an EditText), most @@ -363,6 +365,18 @@ public class EditorInfo implements InputType, Parcelable {      @Nullable      public LocaleList hintLocales = null; + +    /** +     * List of acceptable MIME types for +     * {@link InputConnection#insertContent(InputContentInfo, Bundle)}. +     * +     * <p>{@code null} or an empty array means that +     * {@link InputConnection#insertContent(InputContentInfo, Bundle)} is not supported in this +     * editor.</p> +     */ +    @Nullable +    public String[] contentMimeTypes = null; +      /**       * Ensure that the data in this EditorInfo is compatible with an application       * that was developed against the given target API version.  This can @@ -418,6 +432,7 @@ public class EditorInfo implements InputType, Parcelable {                  + " fieldName=" + fieldName);          pw.println(prefix + "extras=" + extras);          pw.println(prefix + "hintLocales=" + hintLocales); +        pw.println(prefix + "contentMimeTypes=" + Arrays.toString(contentMimeTypes));      }      /** @@ -446,6 +461,7 @@ public class EditorInfo implements InputType, Parcelable {          } else {              LocaleList.getEmptyLocaleList().writeToParcel(dest, flags);          } +        dest.writeStringArray(contentMimeTypes);      }      /** @@ -471,6 +487,7 @@ public class EditorInfo implements InputType, Parcelable {                      res.extras = source.readBundle();                      LocaleList hintLocales = LocaleList.CREATOR.createFromParcel(source);                      res.hintLocales = hintLocales.isEmpty() ? null : hintLocales; +                    res.contentMimeTypes = source.readStringArray();                      return res;                  } diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index 9f6642934768..05e05699df94 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -16,6 +16,8 @@  package android.view.inputmethod; +import android.annotation.NonNull; +import android.annotation.Nullable;  import android.os.Bundle;  import android.os.Handler;  import android.view.KeyCharacterMap; @@ -836,4 +838,33 @@ public interface InputConnection {       * <p>Note: This does nothing when called from input methods.</p>       */      public void closeConnection(); + +    /** +     * Called by the input method to insert a content such as PNG image to the editor. +     * +     * <p>In order to avoid variety of compatibility issues, this focuses on a simple use case, +     * where we expect editors and IMEs work cooperatively as follows:</p> +     * <ul> +     *     <li>Editor must keep {@link EditorInfo#contentMimeTypes} to be {@code null} if it does +     *     not support this method at all.</li> +     *     <li>Editor can ignore this request when the MIME type specified in +     *     {@code inputContentInfo} does not match to any of {@link EditorInfo#contentMimeTypes}. +     *     </li> +     *     <li>Editor can ignore the cursor position when inserting the provided context.</li> +     *     <li>Editor can return {@code true} asynchronously, even before it starts loading the +     *     content.</li> +     *     <li>Editor should provide a way to delete the content inserted by this method, or revert +     *     the effect caused by this method.</li> +     *     <li>IME should not call this method when there is any composing text, in case calling +     *     this method causes focus change.</li> +     *     <li>IME should grant a permission for the editor to read the content. See +     *     {@link EditorInfo#packageName} about how to obtain the package name of the editor.</li> +     * </ul> +     * +     * @param inputContentInfo Content to be inserted. +     * @param opts optional bundle data. This can be {@code null}. +     * @return {@code true} if this request is accepted by the application, no matter if the request +     * is already handled or still being handled in background. +     */ +    public boolean insertContent(@NonNull InputContentInfo inputContentInfo, @Nullable Bundle opts);  } diff --git a/core/java/android/view/inputmethod/InputConnectionInspector.java b/core/java/android/view/inputmethod/InputConnectionInspector.java index 118a61f8d571..fb24fcd8c77a 100644 --- a/core/java/android/view/inputmethod/InputConnectionInspector.java +++ b/core/java/android/view/inputmethod/InputConnectionInspector.java @@ -19,6 +19,7 @@ package android.view.inputmethod;  import android.annotation.IntDef;  import android.annotation.NonNull;  import android.annotation.Nullable; +import android.os.Bundle;  import java.lang.annotation.Retention;  import java.lang.reflect.Method; @@ -41,6 +42,8 @@ public final class InputConnectionInspector {              MissingMethodFlags.REQUEST_CURSOR_UPDATES,              MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS,              MissingMethodFlags.GET_HANDLER, +            MissingMethodFlags.CLOSE_CONNECTION, +            MissingMethodFlags.INSERT_CONTENT,      })      public @interface MissingMethodFlags {          /** @@ -78,6 +81,11 @@ public final class InputConnectionInspector {           * {@link android.os.Build.VERSION_CODES#N} and later.           */          int CLOSE_CONNECTION = 1 << 6; +        /** +         * {@link InputConnection#insertContent(InputContentInfo, Bundle)} is available in +         * {@link android.os.Build.VERSION_CODES#N} MR-1 and later. +         */ +        int INSERT_CONTENT = 1 << 7;      }      private static final Map<Class, Integer> sMissingMethodsMap = Collections.synchronizedMap( @@ -127,6 +135,9 @@ public final class InputConnectionInspector {          if (!hasCloseConnection(clazz)) {              flags |= MissingMethodFlags.CLOSE_CONNECTION;          } +        if (!hasInsertContent(clazz)) { +            flags |= MissingMethodFlags.INSERT_CONTENT; +        }          sMissingMethodsMap.put(clazz, flags);          return flags;      } @@ -195,6 +206,16 @@ public final class InputConnectionInspector {          }      } +    private static boolean hasInsertContent(@NonNull final Class clazz) { +        try { +            final Method method = clazz.getMethod("insertContent", InputContentInfo.class, +                    Bundle.class); +            return !Modifier.isAbstract(method.getModifiers()); +        } catch (NoSuchMethodException e) { +            return false; +        } +    } +      public static String getMissingMethodFlagsAsString(@MissingMethodFlags final int flags) {          final StringBuilder sb = new StringBuilder();          boolean isEmpty = true; @@ -242,6 +263,12 @@ public final class InputConnectionInspector {              }              sb.append("closeConnection()");          } +        if ((flags & MissingMethodFlags.INSERT_CONTENT) != 0) { +            if (!isEmpty) { +                sb.append(","); +            } +            sb.append("InsertContent(InputContentInfo, Bundle)"); +        }          return sb.toString();      }  } diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java index e743f62a5a0a..431836a44f84 100644 --- a/core/java/android/view/inputmethod/InputConnectionWrapper.java +++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java @@ -269,4 +269,12 @@ public class InputConnectionWrapper implements InputConnection {      public void closeConnection() {          mTarget.closeConnection();      } + +    /** +     * {@inheritDoc} +     * @throws NullPointerException if the target is {@code null}. +     */ +    public boolean insertContent(InputContentInfo inputContentInfo, Bundle opts) { +        return mTarget.insertContent(inputContentInfo, opts); +    }  } diff --git a/core/java/android/view/inputmethod/InputContentInfo.aidl b/core/java/android/view/inputmethod/InputContentInfo.aidl new file mode 100644 index 000000000000..1afeee3b4b24 --- /dev/null +++ b/core/java/android/view/inputmethod/InputContentInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.inputmethod; + +parcelable InputContentInfo; diff --git a/core/java/android/view/inputmethod/InputContentInfo.java b/core/java/android/view/inputmethod/InputContentInfo.java new file mode 100644 index 000000000000..e2ecfae7f764 --- /dev/null +++ b/core/java/android/view/inputmethod/InputContentInfo.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.view.inputmethod; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ClipDescription; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +import java.security.InvalidParameterException; + +/** + * A container object with which input methods can send content files to the target application. + */ +public class InputContentInfo implements Parcelable { + +    @NonNull +    private final Uri mContentUri; +    @NonNull +    private final ClipDescription mDescription; +    @Nullable +    private final Uri mLinkUri; + +    /** +     * Constructs {@link InputContentInfo} object only with mandatory data. +     * +     * @param contentUri Content URI to be exported from the input method. +     * This cannot be {@code null}. +     * @param description A {@link ClipDescription} object that contains the metadata of +     * {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also +     * {@link ClipDescription#getLabel()} should be describing the content specified by +     * {@code contentUri} for accessibility reasons. +     */ +    public InputContentInfo(@NonNull Uri contentUri, @NonNull ClipDescription description) { +        this(contentUri, description, null /* link Uri */); +    } + +    /** +     * Constructs {@link InputContentInfo} object with additional link URI. +     * +     * @param contentUri Content URI to be exported from the input method. +     * This cannot be {@code null}. +     * @param description A {@link ClipDescription} object that contains the metadata of +     * {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also +     * {@link ClipDescription#getLabel()} should be describing the content specified by +     * {@code contentUri} for accessibility reasons. +     * @param linkUri An optional {@code http} or {@code https} URI. The editor author may provide +     * a way to navigate the user to the specified web page if this is not {@code null}. +     * @throws InvalidParameterException if any invalid parameter is specified. +     */ +    public InputContentInfo(@NonNull Uri contentUri, @NonNull ClipDescription description, +            @Nullable Uri linkUri) { +        validateInternal(contentUri, description, linkUri, true /* throwException */); +        mContentUri = contentUri; +        mDescription = description; +        mLinkUri = linkUri; +    } + +    /** +     * @return {@code true} if all the fields are valid. +     * @hide +     */ +    public boolean validate() { +        return validateInternal(mContentUri, mDescription, mLinkUri, false /* throwException */); +    } + +    /** +     * Constructs {@link InputContentInfo} object with additional link URI. +     * +     * @param contentUri Content URI to be exported from the input method. +     * This cannot be {@code null}. +     * @param description A {@link ClipDescription} object that contains the metadata of +     * {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also +     * {@link ClipDescription#getLabel()} should be describing the content specified by +     * {@code contentUri} for accessibility reasons. +     * @param linkUri An optional {@code http} or {@code https} URI. The editor author may provide +     * a way to navigate the user to the specified web page if this is not {@code null}. +     * @param throwException {@code true} if this method should throw an +     * {@link InvalidParameterException}. +     * @throws InvalidParameterException if any invalid parameter is specified. +     */ +    private static boolean validateInternal(@NonNull Uri contentUri, +            @NonNull ClipDescription description, @Nullable Uri linkUri, boolean throwException) { +        if (contentUri == null) { +            if (throwException) { +                throw new NullPointerException("contentUri"); +            } +            return false; +        } +        if (description == null) { +            if (throwException) { +                throw new NullPointerException("description"); +            } +            return false; +        } +        final String contentUriScheme = contentUri.getScheme(); +        if (contentUriScheme == null || !contentUriScheme.equalsIgnoreCase("content")) { +            if (throwException) { +                throw new InvalidParameterException("contentUri must have content scheme"); +            } +            return false; +        } +        if (linkUri != null) { +            final String scheme = linkUri.getScheme(); +            if (scheme == null || +                    (!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https"))) { +                if (throwException) { +                    throw new InvalidParameterException( +                            "linkUri must have either http or https scheme"); +                } +                return false; +            } +        } +        return true; +    } + +    /** +     * @return Content URI with which the content can be obtained. +     */ +    @NonNull +    public Uri getContentUri() { return mContentUri; } + +    /** +     * @return {@link ClipDescription} object that contains the metadata of {@code contentUri} such +     * as MIME type(s). {@link ClipDescription#getLabel()} can be used for accessibility purpose. +     */ +    @NonNull +    public ClipDescription getDescription() { return mDescription; } + +    /** +     * @return An optional {@code http} or {@code https} URI that is related to this content. +     */ +    @Nullable +    public Uri getLinkUri() { return mLinkUri; } + +    /** +     * Used to package this object into a {@link Parcel}. +     * +     * @param dest The {@link Parcel} to be written. +     * @param flags The flags used for parceling. +     */ +    @Override +    public void writeToParcel(Parcel dest, int flags) { +        Uri.writeToParcel(dest, mContentUri); +        mDescription.writeToParcel(dest, flags); +        Uri.writeToParcel(dest, mLinkUri); +    } + +    private InputContentInfo(@NonNull Parcel source) { +        mContentUri = Uri.CREATOR.createFromParcel(source); +        mDescription = ClipDescription.CREATOR.createFromParcel(source); +        mLinkUri = Uri.CREATOR.createFromParcel(source); +    } + +    /** +     * Used to make this class parcelable. +     */ +    public static final Parcelable.Creator<InputContentInfo> CREATOR +            = new Parcelable.Creator<InputContentInfo>() { +        @Override +        public InputContentInfo createFromParcel(Parcel source) { +            return new InputContentInfo(source); +        } + +        @Override +        public InputContentInfo[] newArray(int size) { +            return new InputContentInfo[size]; +        } +    }; + +    /** +     * {@inheritDoc} +     */ +    @Override +    public int describeContents() { +        return 0; +    } +} diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index b331be72b4cf..fb532e2dd17f 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -19,6 +19,7 @@ package android.widget;  import android.annotation.ColorInt;  import android.annotation.DrawableRes;  import android.annotation.NonNull; +import android.content.ClipData;  import android.content.Context;  import android.content.Intent;  import android.content.res.TypedArray; @@ -75,6 +76,7 @@ import android.view.inputmethod.EditorInfo;  import android.view.inputmethod.ExtractedText;  import android.view.inputmethod.ExtractedTextRequest;  import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputContentInfo;  import android.view.inputmethod.InputMethodManager;  import android.widget.RemoteViews.OnClickHandler; @@ -5982,6 +5984,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te          public void closeConnection() {              getTarget().closeConnection();          } + +        @Override +        public boolean insertContent(InputContentInfo inputContentInfo, Bundle opts) { +            return getTarget().insertContent(inputContentInfo, opts); +        }      }      /** diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 4a0f3509ad31..c33288bd568d 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -17,6 +17,7 @@  package android.widget;  import android.annotation.ColorInt; +import android.annotation.DimenRes;  import android.app.ActivityManager.StackId;  import android.app.ActivityOptions;  import android.app.ActivityThread; @@ -1850,13 +1851,13 @@ public class RemoteViews implements Parcelable, Filter {      /**       * Helper action to set layout params on a View.       */ -    private class LayoutParamAction extends Action { +    private static class LayoutParamAction extends Action {          /** Set marginEnd */ -        public static final int LAYOUT_MARGIN_END = 1; +        public static final int LAYOUT_MARGIN_END_DIMEN = 1;          /** Set width */          public static final int LAYOUT_WIDTH = 2; -        public static final int LAYOUT_MARGIN_BOTTOM = 3; +        public static final int LAYOUT_MARGIN_BOTTOM_DIMEN = 3;          /**           * @param viewId ID of the view alter @@ -1893,15 +1894,17 @@ public class RemoteViews implements Parcelable, Filter {                  return;              }              switch (property) { -                case LAYOUT_MARGIN_END: +                case LAYOUT_MARGIN_END_DIMEN:                      if (layoutParams instanceof ViewGroup.MarginLayoutParams) { -                        ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(value); +                        int resolved = resolveDimenPixelOffset(target, value); +                        ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(resolved);                          target.setLayoutParams(layoutParams);                      }                      break; -                case LAYOUT_MARGIN_BOTTOM: +                case LAYOUT_MARGIN_BOTTOM_DIMEN:                      if (layoutParams instanceof ViewGroup.MarginLayoutParams) { -                        ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = value; +                        int resolved = resolveDimenPixelOffset(target, value); +                        ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = resolved;                          target.setLayoutParams(layoutParams);                      }                      break; @@ -1914,6 +1917,13 @@ public class RemoteViews implements Parcelable, Filter {              }          } +        private static int resolveDimenPixelOffset(View target, int value) { +            if (value == 0) { +                return 0; +            } +            return target.getContext().getResources().getDimensionPixelOffset(value); +        } +          public String getActionName() {              return "LayoutParamAction" + property + ".";          } @@ -2870,27 +2880,36 @@ public class RemoteViews implements Parcelable, Filter {       * Hidden for now since we don't want to support this for all different layout margins yet.       *       * @param viewId The id of the view to change -     * @param endMargin the left padding in pixels +     * @param endMarginDimen a dimen resource to read the margin from or 0 to clear the margin.       */ -    public void setViewLayoutMarginEnd(int viewId, int endMargin) { -        addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END, endMargin)); +    public void setViewLayoutMarginEndDimen(int viewId, @DimenRes int endMarginDimen) { +        addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END_DIMEN, +                endMarginDimen));      }      /**       * Equivalent to setting {@link android.view.ViewGroup.MarginLayoutParams#bottomMargin}.       * +     * @param bottomMarginDimen a dimen resource to read the margin from or 0 to clear the margin.       * @hide       */ -    public void setViewLayoutMarginBottom(int viewId, int bottomMargin) { -        addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_BOTTOM, -                bottomMargin)); +    public void setViewLayoutMarginBottomDimen(int viewId, @DimenRes int bottomMarginDimen) { +        addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_BOTTOM_DIMEN, +                bottomMarginDimen));      }      /**       * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width}. +     * +     * @param layoutWidth one of 0, MATCH_PARENT or WRAP_CONTENT. Other sizes are not allowed +     *                    because they behave poorly when the density changes.       * @hide       */      public void setViewLayoutWidth(int viewId, int layoutWidth) { +        if (layoutWidth != 0 && layoutWidth != ViewGroup.LayoutParams.MATCH_PARENT +                && layoutWidth != ViewGroup.LayoutParams.WRAP_CONTENT) { +            throw new IllegalArgumentException("Only supports 0, WRAP_CONTENT and MATCH_PARENT"); +        }          mActions.add(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, layoutWidth));      } diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 59b4b35fa24e..fc189a3e909e 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -33,6 +33,7 @@ import android.view.inputmethod.ExtractedTextRequest;  import android.view.inputmethod.InputConnection;  import android.view.inputmethod.InputConnectionInspector;  import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags; +import android.view.inputmethod.InputContentInfo;  public abstract class IInputConnectionWrapper extends IInputContext.Stub {      static final String TAG = "IInputConnectionWrapper"; @@ -61,6 +62,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {      private static final int DO_CLEAR_META_KEY_STATES = 130;      private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;      private static final int DO_CLOSE_CONNECTION = 150; +    private static final int DO_INSERT_CONTENT = 160;      @GuardedBy("mLock")      @Nullable @@ -241,6 +243,11 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {          dispatchMessage(obtainMessage(DO_CLOSE_CONNECTION));      } +    public void insertContent(InputContentInfo inputContentInfo, Bundle opts, +            int seq, IInputContextCallback callback) { +        dispatchMessage(obtainMessageOOSC(DO_INSERT_CONTENT, inputContentInfo, opts, seq, callback)); +    } +      void dispatchMessage(Message msg) {          // If we are calling this from the main thread, then we can call          // right through.  Otherwise, we need to send the message to the @@ -552,6 +559,29 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {                  }                  return;              } +            case DO_INSERT_CONTENT: { +                SomeArgs args = (SomeArgs) msg.obj; +                try { +                    InputConnection ic = getInputConnection(); +                    if (ic == null || !isActive()) { +                        Log.w(TAG, "insertContent on inactive InputConnection"); +                        args.callback.setInsertContentResult(false, args.seq); +                        return; +                    } +                    final InputContentInfo inputContentInfo = (InputContentInfo) args.arg1; +                    if (inputContentInfo == null || !inputContentInfo.validate()) { +                        Log.w(TAG, "insertContent with invalid inputContentInfo=" +                                + inputContentInfo); +                        args.callback.setInsertContentResult(false, args.seq); +                        return; +                    } +                    args.callback.setInsertContentResult( +                            ic.insertContent(inputContentInfo, (Bundle) args.arg2), args.seq); +                } catch (RemoteException e) { +                    Log.w(TAG, "Got RemoteException calling insertContent", e); +                } +                return; +            }          }          Log.w(TAG, "Unhandled message code: " + msg.what);      } @@ -582,9 +612,11 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {          return mH.obtainMessage(what, arg1, arg2, args);      } -    Message obtainMessageOSC(int what, Object arg1, int seq, IInputContextCallback callback) { +    Message obtainMessageOOSC(int what, Object arg1, Object arg2, int seq, +            IInputContextCallback callback) {          SomeArgs args = new SomeArgs();          args.arg1 = arg1; +        args.arg2 = arg2;          args.callback = callback;          args.seq = seq;          return mH.obtainMessage(what, 0, 0, args); diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl index f7ec420ba676..bd473557a1b6 100644 --- a/core/java/com/android/internal/view/IInputContext.aidl +++ b/core/java/com/android/internal/view/IInputContext.aidl @@ -21,6 +21,7 @@ import android.view.KeyEvent;  import android.view.inputmethod.CompletionInfo;  import android.view.inputmethod.CorrectionInfo;  import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.InputContentInfo;  import com.android.internal.view.IInputContextCallback; @@ -74,6 +75,9 @@ import com.android.internal.view.IInputContextCallback;      void getSelectedText(int flags, int seq, IInputContextCallback callback); -    void requestUpdateCursorAnchorInfo(in int cursorUpdateMode, int seq, +    void requestUpdateCursorAnchorInfo(int cursorUpdateMode, int seq, +            IInputContextCallback callback); + +    void insertContent(in InputContentInfo inputContentInfo, in Bundle opts, int sec,              IInputContextCallback callback);  } diff --git a/core/java/com/android/internal/view/IInputContextCallback.aidl b/core/java/com/android/internal/view/IInputContextCallback.aidl index 54ea3064bd7e..933cdc0d8745 100644 --- a/core/java/com/android/internal/view/IInputContextCallback.aidl +++ b/core/java/com/android/internal/view/IInputContextCallback.aidl @@ -28,4 +28,5 @@ oneway interface IInputContextCallback {      void setExtractedText(in ExtractedText extractedText, int seq);      void setSelectedText(CharSequence selectedText, int seq);      void setRequestUpdateCursorAnchorInfoResult(boolean result, int seq); +    void setInsertContentResult(boolean result, int seq);  } diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index f9884d85d52c..6d4d45c7041f 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -16,6 +16,7 @@  package com.android.internal.view; +import android.content.ClipData;  import android.os.Bundle;  import android.os.Handler;  import android.os.RemoteException; @@ -29,6 +30,7 @@ import android.view.inputmethod.ExtractedTextRequest;  import android.view.inputmethod.InputConnection;  import android.view.inputmethod.InputConnectionInspector;  import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags; +import android.view.inputmethod.InputContentInfo;  public class InputConnectionWrapper implements InputConnection {      private static final int MAX_WAIT_TIME_MILLIS = 2000; @@ -46,7 +48,8 @@ public class InputConnectionWrapper implements InputConnection {          public ExtractedText mExtractedText;          public int mCursorCapsMode;          public boolean mRequestUpdateCursorAnchorInfoResult; -         +        public boolean mInsertContentResult; +          // A 'pool' of one InputContextCallback.  Each ICW request will attempt to gain          // exclusive access to this object.          private static InputContextCallback sInstance = new InputContextCallback(); @@ -172,6 +175,19 @@ public class InputConnectionWrapper implements InputConnection {              }          } +        public void setInsertContentResult(boolean result, int seq) { +            synchronized (this) { +                if (seq == mSeq) { +                    mInsertContentResult = result; +                    mHaveValue = true; +                    notifyAll(); +                } else { +                    Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq +                            + ") in setInsertContentResult, ignoring."); +                } +            } +        } +          /**           * Waits for a result for up to {@link #MAX_WAIT_TIME_MILLIS} milliseconds.           *  @@ -491,6 +507,28 @@ public class InputConnectionWrapper implements InputConnection {          // Nothing should happen when called from input method.      } +    public boolean insertContent(InputContentInfo inputContentInfo, Bundle opts) { +        boolean result = false; +        if (isMethodMissing(MissingMethodFlags.INSERT_CONTENT)) { +            // This method is not implemented. +            return false; +        } +        try { +            InputContextCallback callback = InputContextCallback.getInstance(); +            mIInputContext.insertContent(inputContentInfo, opts, callback.mSeq, callback); +            synchronized (callback) { +                callback.waitForResultLocked(); +                if (callback.mHaveValue) { +                    result = callback.mInsertContentResult; +                } +            } +            callback.dispose(); +        } catch (RemoteException e) { +            return false; +        } +        return result; +    } +      private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) {          return (mMissingMethods & methodFlag) == methodFlag;      } diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java index dc7b7f5b9646..d2a43b755f18 100644 --- a/core/java/com/android/internal/widget/MessagingLinearLayout.java +++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java @@ -48,6 +48,11 @@ public class MessagingLinearLayout extends ViewGroup {      private int mIndentLines; +    /** +     * Id of the child that's also visible in the contracted layout. +     */ +    private int mContractedChildId; +      public MessagingLinearLayout(Context context, @Nullable AttributeSet attrs) {          super(context, attrs); @@ -255,14 +260,29 @@ public class MessagingLinearLayout extends ViewGroup {          return copy;      } -    @RemotableViewMethod      /**       * Sets how many lines should be indented to avoid a floating image.       */ +    @RemotableViewMethod      public void setNumIndentLines(int numberLines) {          mIndentLines = numberLines;      } +    /** +     * Set id of the child that's also visible in the contracted layout. +     */ +    @RemotableViewMethod +    public void setContractedChildId(int contractedChildId) { +        mContractedChildId = contractedChildId; +    } + +    /** +     * Get id of the child that's also visible in the contracted layout. +     */ +    public int getContractedChildId() { +        return mContractedChildId; +    } +      public static class LayoutParams extends MarginLayoutParams {          boolean hide = false; diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml index 4670dcabe211..caeb43ac5bbe 100644 --- a/core/res/res/layout/notification_material_action_list.xml +++ b/core/res/res/layout/notification_material_action_list.xml @@ -17,7 +17,8 @@  <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"          android:id="@+id/actions_container"          android:layout_width="match_parent" -        android:layout_height="wrap_content"> +        android:layout_height="wrap_content" +        android:layout_gravity="bottom">      <com.android.internal.widget.NotificationActionListLayout              android:id="@+id/actions"              android:layout_width="match_parent" diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml index c54fa1895dfa..8b0b4763d7fa 100644 --- a/core/res/res/layout/notification_template_material_big_base.xml +++ b/core/res/res/layout/notification_template_material_big_base.xml @@ -14,7 +14,7 @@    ~ See the License for the specific language governing permissions and    ~ limitations under the License    --> -<LinearLayout +<FrameLayout      xmlns:android="http://schemas.android.com/apk/res/android"      android:id="@+id/status_bar_latest_event_content"      android:layout_width="match_parent" @@ -22,43 +22,49 @@      android:orientation="vertical"      android:tag="big"      > -    <FrameLayout -        android:id="@+id/status_bar_latest_event_content" -        android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:layout_gravity="top" -        android:tag="base" -        > -        <include layout="@layout/notification_template_header" /> -        <LinearLayout -            android:id="@+id/notification_main_column" +    <LinearLayout +            android:id="@+id/notification_action_list_margin_target"              android:layout_width="match_parent" -            android:layout_height="match_parent" -            android:layout_gravity="top" -            android:layout_marginStart="@dimen/notification_content_margin_start" -            android:layout_marginEnd="@dimen/notification_content_margin_end" -            android:layout_marginTop="@dimen/notification_content_margin_top" -            android:layout_marginBottom="@dimen/notification_content_margin_bottom" +            android:layout_height="wrap_content" +            android:layout_marginBottom="@dimen/notification_action_list_height"              android:orientation="vertical"              > -            <include layout="@layout/notification_template_part_line1" /> -            <include layout="@layout/notification_template_text" /> -        </LinearLayout>          <FrameLayout              android:layout_width="match_parent"              android:layout_height="wrap_content" -            android:layout_gravity="bottom" -            android:layout_marginStart="@dimen/notification_content_margin_start" -            android:layout_marginBottom="15dp" -            android:layout_marginEnd="@dimen/notification_content_margin_end"> -            <include layout="@layout/notification_template_progress" /> +            android:layout_gravity="top" +            > +            <include layout="@layout/notification_template_header" /> +            <LinearLayout +                android:id="@+id/notification_main_column" +                android:layout_width="match_parent" +                android:layout_height="match_parent" +                android:layout_gravity="top" +                android:layout_marginStart="@dimen/notification_content_margin_start" +                android:layout_marginEnd="@dimen/notification_content_margin_end" +                android:layout_marginTop="@dimen/notification_content_margin_top" +                android:layout_marginBottom="@dimen/notification_content_margin_bottom" +                android:orientation="vertical" +                > +                <include layout="@layout/notification_template_part_line1" /> +                <include layout="@layout/notification_template_text" /> +            </LinearLayout> +            <FrameLayout +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:layout_gravity="bottom" +                android:layout_marginStart="@dimen/notification_content_margin_start" +                android:layout_marginBottom="15dp" +                android:layout_marginEnd="@dimen/notification_content_margin_end"> +                <include layout="@layout/notification_template_progress" /> +            </FrameLayout> +            <include layout="@layout/notification_template_right_icon" />          </FrameLayout> -        <include layout="@layout/notification_template_right_icon" /> -    </FrameLayout> -    <ViewStub android:layout="@layout/notification_material_reply_text" -            android:id="@+id/notification_material_reply_container" -            android:layout_width="match_parent" -            android:layout_height="wrap_content" -    /> +        <ViewStub android:layout="@layout/notification_material_reply_text" +                android:id="@+id/notification_material_reply_container" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +        /> +    </LinearLayout>      <include layout="@layout/notification_material_action_list" /> -</LinearLayout> +</FrameLayout> diff --git a/core/res/res/layout/notification_template_material_big_picture.xml b/core/res/res/layout/notification_template_material_big_picture.xml index d87b9d90e48f..83c0fec004b6 100644 --- a/core/res/res/layout/notification_template_material_big_picture.xml +++ b/core/res/res/layout/notification_template_material_big_picture.xml @@ -30,6 +30,7 @@              android:layout_marginTop="@dimen/notification_content_margin_top"              android:clipToPadding="false"              android:orientation="vertical" +            android:id="@+id/notification_action_list_margin_target"              >          <LinearLayout              android:id="@+id/notification_main_column" @@ -60,6 +61,6 @@                  android:layout_width="match_parent"                  android:layout_height="wrap_content"                  /> -        <include layout="@layout/notification_material_action_list" />      </LinearLayout> +    <include layout="@layout/notification_material_action_list" />  </FrameLayout> diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml index 71600ef7b134..f4f783ecc6fb 100644 --- a/core/res/res/layout/notification_template_material_big_text.xml +++ b/core/res/res/layout/notification_template_material_big_text.xml @@ -24,10 +24,12 @@      <include layout="@layout/notification_template_header" />      <LinearLayout +            android:id="@+id/notification_action_list_margin_target"              android:layout_width="match_parent"              android:layout_height="match_parent"              android:layout_gravity="top"              android:layout_marginTop="@dimen/notification_content_margin_top" +            android:layout_marginBottom="@dimen/notification_action_list_height"              android:clipToPadding="false"              android:orientation="vertical"> @@ -61,7 +63,7 @@                  android:id="@+id/notification_material_reply_container"                  android:layout_width="match_parent"                  android:layout_height="wrap_content" /> -        <include layout="@layout/notification_material_action_list" />      </LinearLayout> +    <include layout="@layout/notification_material_action_list" />      <include layout="@layout/notification_template_right_icon" />  </FrameLayout> diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml index 0e93d0bc1635..7820e39e777c 100644 --- a/core/res/res/layout/notification_template_material_inbox.xml +++ b/core/res/res/layout/notification_template_material_inbox.xml @@ -23,6 +23,7 @@      >      <include layout="@layout/notification_template_header" />      <LinearLayout +            android:id="@+id/notification_action_list_margin_target"              android:layout_width="match_parent"              android:layout_height="match_parent"              android:layout_gravity="top" @@ -117,7 +118,7 @@                  android:id="@+id/notification_material_reply_container"                  android:layout_width="match_parent"                  android:layout_height="wrap_content" /> -        <include layout="@layout/notification_material_action_list" />      </LinearLayout> +    <include layout="@layout/notification_material_action_list" />      <include layout="@layout/notification_template_right_icon" />  </FrameLayout> diff --git a/core/res/res/layout/notification_template_material_messaging.xml b/core/res/res/layout/notification_template_material_messaging.xml index d95ff056c22a..07b1100ee93b 100644 --- a/core/res/res/layout/notification_template_material_messaging.xml +++ b/core/res/res/layout/notification_template_material_messaging.xml @@ -22,6 +22,7 @@      >      <include layout="@layout/notification_template_header" />      <LinearLayout +            android:id="@+id/notification_action_list_margin_target"              android:layout_width="match_parent"              android:layout_height="match_parent"              android:layout_gravity="top" @@ -74,7 +75,7 @@                      />              </com.android.internal.widget.MessagingLinearLayout>          </LinearLayout> -        <include layout="@layout/notification_material_action_list" />      </LinearLayout> +    <include layout="@layout/notification_material_action_list" />      <include layout="@layout/notification_template_right_icon" />  </FrameLayout> diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml index 879e02ec33a5..2313b26c1f8d 100644 --- a/core/res/res/values-watch/themes_device_defaults.xml +++ b/core/res/res/values-watch/themes_device_defaults.xml @@ -38,4 +38,6 @@ easier.          <item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>      </style> +    <!-- Use a dark theme for watches. --> +    <style name="Theme.DeviceDefault.System" parent="Theme.Material" />  </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 37fb8166e989..91d7227c6a60 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -148,12 +148,20 @@      <!-- The margin on the start of the content view -->      <dimen name="notification_content_margin_start">16dp</dimen> -    <!-- The margin on the end of the content view --> +    <!-- The margin on the end of the content view +        Keep in sync with notification_content_plus_picture_margin! -->      <dimen name="notification_content_margin_end">16dp</dimen> -    <!-- The margin on the end of the content view with a picture.--> +    <!-- The margin on the end of the content view with a picture. +        Keep in sync with notification_content_plus_picture_margin! -->      <dimen name="notification_content_picture_margin">56dp</dimen> +    <!-- The margin on the end of the content view with a picture, plus the standard +        content end margin. +        Keep equal to (notification_content_picture_margin + notification_content_margin_end)! +    --> +    <dimen name="notification_content_plus_picture_margin_end">72dp</dimen> +      <!-- The height of the notification action list -->      <dimen name="notification_action_list_height">56dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 8a88874fd8d1..b5a53efcf7c3 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2188,6 +2188,7 @@    <java-symbol type="style" name="TextAppearance.Material.TimePicker.TimeLabel" />    <java-symbol type="attr" name="seekBarPreferenceStyle" />    <java-symbol type="style" name="Theme.DeviceDefault.Resolver" /> +  <java-symbol type="style" name="Theme.DeviceDefault.System" />    <java-symbol type="attr" name="preferenceActivityStyle" />    <java-symbol type="attr" name="preferenceFragmentStyle" />    <java-symbol type="bool" name="skipHoldBeforeMerge" /> @@ -2607,6 +2608,9 @@    <java-symbol type="bool" name="config_supportPreRebootSecurityLogs" /> +  <java-symbol type="dimen" name="notification_content_plus_picture_margin_end" /> +  <java-symbol type="id" name="notification_action_list_margin_target" /> +    <!-- Pinner Service -->    <java-symbol type="array" name="config_defaultPinnerServiceFiles" /> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 11bb106a6873..ea1b626a996a 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -551,4 +551,6 @@ easier.          <item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>      </style> +    <!-- DeviceDefault theme for the default system theme.  --> +    <style name="Theme.DeviceDefault.System" parent="Theme.Material.Light.DarkActionBar" />  </resources> diff --git a/docs/html-intl/intl/es/training/material/compatibility.jd b/docs/html-intl/intl/es/training/material/compatibility.jd index ad2e953fed4f..d2cb9aacb622 100644 --- a/docs/html-intl/intl/es/training/material/compatibility.jd +++ b/docs/html-intl/intl/es/training/material/compatibility.jd @@ -123,7 +123,7 @@ con esquinas redondeadas.</li>  <h3>Dependencias</h3>  <p>Para usar estas caracterÃsticas en versiones de Android anteriores a la 5.0 (API nivel 21), incluye -en tu proyecto la Biblioteca de soporte v7 de Android como una <a href="{@docRoot}/sdk/installing/studio-build.html#dependencies">dependencia de Gradle</a>:</p> +en tu proyecto la Biblioteca de soporte v7 de Android como una <a href="{@docRoot}sdk/installing/studio-build.html#dependencies">dependencia de Gradle</a>:</p>  <pre>  dependencies { diff --git a/docs/html-intl/intl/in/training/material/compatibility.jd b/docs/html-intl/intl/in/training/material/compatibility.jd index d57c7bef1810..ef444c359b57 100644 --- a/docs/html-intl/intl/in/training/material/compatibility.jd +++ b/docs/html-intl/intl/in/training/material/compatibility.jd @@ -123,7 +123,7 @@ Android v7 Support Library dengan pembatasan ini:</p>  <h3>Dependensi</h3>  <p>Untuk menggunakan fitur-fitur ini di versi Android sebelum 5.0 (API level 21), sertakan -Android v7 Support Library dalam proyek Anda sebagai <a href="{@docRoot}/sdk/installing/studio-build.html#dependencies">dependensi Gradle</a>:</p> +Android v7 Support Library dalam proyek Anda sebagai <a href="{@docRoot}sdk/installing/studio-build.html#dependencies">dependensi Gradle</a>:</p>  <pre>  dependencies { diff --git a/docs/html-intl/intl/ja/training/material/compatibility.jd b/docs/html-intl/intl/ja/training/material/compatibility.jd index 0f8922fec4a3..25811704aac2 100644 --- a/docs/html-intl/intl/ja/training/material/compatibility.jd +++ b/docs/html-intl/intl/ja/training/material/compatibility.jd @@ -122,7 +122,7 @@ android.support.v7.widget.CardView} ウィジェット㯠Android v7 サãƒãƒ¼ãƒ  <h3>ä¾å˜é–¢ä¿‚</h3> -<p>5.0(API レベル 21)よりå‰ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã® Android ã§ã“ã‚Œã‚‰ã®æ©Ÿèƒ½ã‚’使用ã™ã‚‹ã«ã¯ã€Android v7 サãƒãƒ¼ãƒˆ ライブラリを <a href="{@docRoot}/sdk/installing/studio-build.html#dependencies">Gradle ä¾å˜é–¢ä¿‚</a>ã¨ã—ã¦ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã«å«ã‚ã¾ã™ã€‚ +<p>5.0(API レベル 21)よりå‰ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã® Android ã§ã“ã‚Œã‚‰ã®æ©Ÿèƒ½ã‚’使用ã™ã‚‹ã«ã¯ã€Android v7 サãƒãƒ¼ãƒˆ ライブラリを <a href="{@docRoot}sdk/installing/studio-build.html#dependencies">Gradle ä¾å˜é–¢ä¿‚</a>ã¨ã—ã¦ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã«å«ã‚ã¾ã™ã€‚  </p>  <pre> diff --git a/docs/html-intl/intl/ko/training/material/compatibility.jd b/docs/html-intl/intl/ko/training/material/compatibility.jd index 266cd7c62f9b..5bb84340c4f5 100644 --- a/docs/html-intl/intl/ko/training/material/compatibility.jd +++ b/docs/html-intl/intl/ko/training/material/compatibility.jd @@ -122,7 +122,7 @@ android.support.v7.widget.CardView} ìœ„ì ¯ì€ Android v7 ì§€ì› ë¼ì´ë¸ŒëŸ¬ë¦¬ë  <h3>ì¢…ì† ì‚¬í•</h3> -<p>Android 5.0(API 레벨 21) ì´ì „ 버전ì—서 ì´ëŸ¬í•œ ê¸°ëŠ¥ì„ ì‚¬ìš©í•˜ë ¤ë©´ 프로ì íŠ¸ì— Android v7 ì§€ì› ë¼ì´ë¸ŒëŸ¬ë¦¬ë¥¼ <a href="{@docRoot}/sdk/installing/studio-build.html#dependencies">Gradle ì¢…ì† ì‚¬í•</a>으로 í¬í•¨í•©ë‹ˆë‹¤. +<p>Android 5.0(API 레벨 21) ì´ì „ 버전ì—서 ì´ëŸ¬í•œ ê¸°ëŠ¥ì„ ì‚¬ìš©í•˜ë ¤ë©´ 프로ì íŠ¸ì— Android v7 ì§€ì› ë¼ì´ë¸ŒëŸ¬ë¦¬ë¥¼ <a href="{@docRoot}sdk/installing/studio-build.html#dependencies">Gradle ì¢…ì† ì‚¬í•</a>으로 í¬í•¨í•©ë‹ˆë‹¤.  </p>  <pre> diff --git a/docs/html-intl/intl/pt-br/training/material/compatibility.jd b/docs/html-intl/intl/pt-br/training/material/compatibility.jd index 2540df118aeb..d242c0cf1641 100644 --- a/docs/html-intl/intl/pt-br/training/material/compatibility.jd +++ b/docs/html-intl/intl/pt-br/training/material/compatibility.jd @@ -123,7 +123,7 @@ Biblioteca de Suporte v7 do Android com as seguintes limitações:</p>  <h3>Dependências</h3>  <p>Para usar esses recursos em versões anteriores ao Android 5.0 (API de nÃvel 21), inclua a -Biblioteca de Suporte v7 do Android no projeto como uma <a href="{@docRoot}/sdk/installing/studio-build.html#dependencies">dependência de Gradle</a>:</p> +Biblioteca de Suporte v7 do Android no projeto como uma <a href="{@docRoot}sdk/installing/studio-build.html#dependencies">dependência de Gradle</a>:</p>  <pre>  dependencies { diff --git a/docs/html-intl/intl/ru/training/material/compatibility.jd b/docs/html-intl/intl/ru/training/material/compatibility.jd index b7ca338045fa..1f1d63053891 100644 --- a/docs/html-intl/intl/ru/training/material/compatibility.jd +++ b/docs/html-intl/intl/ru/training/material/compatibility.jd @@ -123,7 +123,7 @@ android.support.v7.widget.CardView} имеютÑÑ Ð² более ранних в  <h3>ЗавиÑимоÑти</h3>  <p>Чтобы воÑпользоватьÑÑ Ñтими возможноÑÑ‚Ñми в верÑиÑÑ… Android, предшеÑтвующих 5.0 (уровень API 21), включите в Ñвой проект вÑпомогательную библиотеку -Android v7 как <a href="{@docRoot}/sdk/installing/studio-build.html#dependencies">завиÑимоÑть Gradle</a>:</p> +Android v7 как <a href="{@docRoot}sdk/installing/studio-build.html#dependencies">завиÑимоÑть Gradle</a>:</p>  <pre>  dependencies { diff --git a/docs/html-intl/intl/vi/training/material/compatibility.jd b/docs/html-intl/intl/vi/training/material/compatibility.jd index 65e813129704..e19a745dd9b9 100644 --- a/docs/html-intl/intl/vi/training/material/compatibility.jd +++ b/docs/html-intl/intl/vi/training/material/compatibility.jd @@ -123,7 +123,7 @@ Thư viện Há»— trợ v7 cá»§a Android vá»›i những hạn chế sau:</p>  <h3>Phụ thuá»™c</h3>  <p>Äể sá» dụng những tÃnh năng này trong các phiên bản Android trước 5.0 (API mức 21), hãy thêm -Thư viện Há»— trợ v7 cá»§a Android vào dá»± án cá»§a bạn như má»™t <a href="{@docRoot}/sdk/installing/studio-build.html#dependencies">Phần phụ thuá»™c Gradle</a>:</p> +Thư viện Há»— trợ v7 cá»§a Android vào dá»± án cá»§a bạn như má»™t <a href="{@docRoot}sdk/installing/studio-build.html#dependencies">Phần phụ thuá»™c Gradle</a>:</p>  <pre>  dependencies { diff --git a/docs/html-intl/intl/zh-cn/training/material/compatibility.jd b/docs/html-intl/intl/zh-cn/training/material/compatibility.jd index aa23d4b5e614..9ba85691f22f 100644 --- a/docs/html-intl/intl/zh-cn/training/material/compatibility.jd +++ b/docs/html-intl/intl/zh-cn/training/material/compatibility.jd @@ -123,7 +123,7 @@ android.support.v7.widget.CardView} å°ç»„ä»¶å¯é€šè¿‡ Android v7 支æŒå†…å®¹åº  <h3>ä¾èµ–项</h3>  <p>如果è¦åœ¨ Android 5.0(API 级别 21)之å‰çš„ Android 版本ä¸ä½¿ç”¨è¿™äº›åŠŸèƒ½ï¼Œè¯·å°† -Android v7 支æŒå†…容库作为 <a href="{@docRoot}/sdk/installing/studio-build.html#dependencies">Gradle ä¾èµ–项</a>包括在您的项目ä¸ï¼š</p> +Android v7 支æŒå†…容库作为 <a href="{@docRoot}sdk/installing/studio-build.html#dependencies">Gradle ä¾èµ–项</a>包括在您的项目ä¸ï¼š</p>  <pre>  dependencies { diff --git a/docs/html-intl/intl/zh-tw/training/material/compatibility.jd b/docs/html-intl/intl/zh-tw/training/material/compatibility.jd index 767788bf75be..7d3cddeb5413 100644 --- a/docs/html-intl/intl/zh-tw/training/material/compatibility.jd +++ b/docs/html-intl/intl/zh-tw/training/material/compatibility.jd @@ -122,7 +122,7 @@ android.support.v7.widget.CardView} å°å·¥å…·ï¼Œä½†æœ‰ä¸‹åˆ—é™åˆ¶ï¼š  <h3>ç›¸ä¾æ€§</h3> -<p>如果è¦åœ¨ Android 5.0 (API 級別 21) 以å‰çš„版本ä¸ä½¿ç”¨é€™äº›åŠŸèƒ½ï¼Œè«‹åœ¨æ‚¨çš„å°ˆæ¡ˆä¸åŒ…å« Android v7 支æ´ç¨‹å¼åº«åšç‚º <a href="{@docRoot}/sdk/installing/studio-build.html#dependencies">Gradle ç›¸ä¾æ€§</a>: +<p>如果è¦åœ¨ Android 5.0 (API 級別 21) 以å‰çš„版本ä¸ä½¿ç”¨é€™äº›åŠŸèƒ½ï¼Œè«‹åœ¨æ‚¨çš„å°ˆæ¡ˆä¸åŒ…å« Android v7 支æ´ç¨‹å¼åº«åšç‚º <a href="{@docRoot}sdk/installing/studio-build.html#dependencies">Gradle ç›¸ä¾æ€§</a>:  </p>  <pre> diff --git a/docs/html/guide/practices/screens_support.jd b/docs/html/guide/practices/screens_support.jd index 4d93c835035e..2223dbf3096d 100644 --- a/docs/html/guide/practices/screens_support.jd +++ b/docs/html/guide/practices/screens_support.jd @@ -988,7 +988,7 @@ configurations.</p>  on different screens:</p>  <ol> -  <li>Use {@code wrap_content}, {@code fill_parent}, or {@code dp} units when specifying +  <li>Use {@code wrap_content}, {@code match_parent}, or {@code dp} units when specifying  dimensions in an XML layout file</li>    <li>Do not use hard coded pixel values in your application code</li>    <li>Do not use {@code AbsoluteLayout} (it's deprecated)</li> @@ -998,7 +998,7 @@ dimensions in an XML layout file</li>  <p>The following sections provide more details.</p> -<h3 id="use-relative">1. Use wrap_content, fill_parent, or the dp unit for layout dimensions</h3> +<h3 id="use-relative">1. Use wrap_content, match_parent, or the dp unit for layout dimensions</h3>  <p>When defining the <a  href="{@docRoot}reference/android/view/ViewGroup.LayoutParams.html#attr_android:layout_width" @@ -1006,7 +1006,7 @@ href="{@docRoot}reference/android/view/ViewGroup.LayoutParams.html#attr_android:  href="{@docRoot}reference/android/view/ViewGroup.LayoutParams.html#attr_android:layout_height"  >{@code android:layout_height}</a> for  views in an XML layout file, using <code>"wrap_content"</code>, -<code>"fill_parent"</code> or <code>dp</code> units guarantees that the view is +<code>"match_parent"</code> or <code>dp</code> units guarantees that the view is  given an appropriate size on the current device screen.</p>  <p>For instance, a view with a <code>layout_width="100dp"</code> measures 100 pixels wide on diff --git a/docs/html/guide/practices/verifying-apps-art.jd b/docs/html/guide/practices/verifying-apps-art.jd index 8a88222ee2a0..217c65c9fcd2 100644 --- a/docs/html/guide/practices/verifying-apps-art.jd +++ b/docs/html/guide/practices/verifying-apps-art.jd @@ -63,7 +63,7 @@ ART. This document discusses some of these issues.</p>  java.lang.System#gc() System.gc()} to prompt garbage collection (GC). This should be  far less necessary with ART, particularly if you're invoking garbage collection  to prevent <a -href="{@docRoot}/tools/debugging/debugging-memory.html#LogMessages"><code>GC_FOR_ALLOC</code></a>-type +href="{@docRoot}tools/debugging/debugging-memory.html#LogMessages"><code>GC_FOR_ALLOC</code></a>-type  occurrences or to reduce fragmentation. You can verify which runtime is in use  by calling {@link java.lang.System#getProperty(java.lang.String)  System.getProperty("java.vm.version")}. If ART is in use, the property's value diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd index e0f5e3d2fc25..f30263e8983c 100644 --- a/docs/html/guide/topics/manifest/activity-element.jd +++ b/docs/html/guide/topics/manifest/activity-element.jd @@ -714,7 +714,7 @@ For example:</p>  <activity      android:name="com.example.app.ChildActivity"      android:label="@string/title_child_activity" -    android:parentActivityName="com.example.myfirstapp.MainActivity" > +    android:parentActivityName="com.example.app.MainActivity" >      <!-- Parent activity meta-data to support API level 4+ -->      <meta-data          android:name="android.support.PARENT_ACTIVITY" diff --git a/docs/html/jd_extras_en.js b/docs/html/jd_extras_en.js index 6295e0efd00c..21379292bf36 100644 --- a/docs/html/jd_extras_en.js +++ b/docs/html/jd_extras_en.js @@ -5379,10 +5379,15 @@ METADATA['en'].collections = {        "preview/support.html"      ]    }, +  "preview/landing/videos/first": { +    "title": "", +    "resources": [ +    "https://www.youtube.com/watch?v=CsulIu3UaUM" +    ] +  },    "preview/landing/more": {      "title": "",      "resources": [ -      "https://www.youtube.com/watch?v=CsulIu3UaUM",        "preview/features/multi-window.html",        "preview/features/notification-updates.html",        "preview/features/background-optimization.html", diff --git a/docs/html/preview/_book.yaml b/docs/html/preview/_book.yaml index 0d4b81b61cfb..ad6724968554 100644 --- a/docs/html/preview/_book.yaml +++ b/docs/html/preview/_book.yaml @@ -220,6 +220,8 @@ toc:        value: TV 录制      - name: zh-tw-lang        value: 電視錄製 +  - title: Key Attestation +    path: /preview/features/key-attestation.html    - title: Network Security Configuration      path: /preview/features/security-config.html      path_attributes: diff --git a/docs/html/preview/api-overview.jd b/docs/html/preview/api-overview.jd index d457d5cb4877..4abd95634b90 100644 --- a/docs/html/preview/api-overview.jd +++ b/docs/html/preview/api-overview.jd @@ -701,48 +701,37 @@ before unlock. All other data is unavailable until the User confirms their lock  For more information, see <a href="{@docRoot}preview/features/direct-boot.html">Direct Boot</a>.</p>  </p> -  <h2 id="key_attestation">Key Attestation</h2> -<p>Hardware-backed keystores provide a much safer method to create, store, -and use cryptographic keys on Android devices. They protect keys from the -Linux kernel, potential Android vulnerabilities, and extraction -from rooted devices.</p> - -<p>To make it easier and more secure to use hardware-backed keystores, -Android N introduces Key Attestation. Apps and off-devices can use Key -Attestation to strongly determine whether an RSA or EC key pair is -hardware-backed, what the properties of the key pair are, and what -  constraints are applied to its usage and validity. </p> - -<p>Apps and off-device services can request information about a key pair -through an X.509 attestation certificate which must be signed by a valid -attestation key. The attestation key is an ECDSA signing key which is -injected into the device’s hardware-backed keystore at the factory. -Therefore, an attestation certificate signed by a valid attestation -key confirms the existence of a hardware-backed keystore, along with -  details of key pairs in that keystore.</p> - -<p>To ensure that the device is using a secure, official Android factory -image, Key Attestation requires that the device <a -class="external-link" -href="https://source.android.com/security/verifiedboot/verified-boot.html#bootloader_requirements">bootloader</a> -provide the following information to the <a class="external-link" -href="https://source.android.com/security/trusty/index.html">Trusted -Execution Environment (TEE)</a>:</p> - -<ul> -<li>The OS version and patch level installed on the device</li> -<li>The <a href="https://source.android.com/security/verifiedboot/index.html" -class="external-link" >Verified Boot</a> public key and lock status</li> -  </ul> +<p> +  Android N introduces <em>key attestation</em>, a new security tool that helps +  you make sure that the key pairs stored within a device's <a class= +  "external-link" href= +  "https://source.android.com/security/keystore/"><em>hardware-backed +  keystore</em></a> properly protect the sensitive information that your app +  uses. By using this tool, you gain additional confidence that your app +  interacts with keys that reside in secure hardware, even if the device +  running your app is rooted. If you use keys from the hardware-backed keystore +  in your apps, you should use this tool, particularly if you use the keys to +  verify sensitive information within your app. +</p> -<p>For more information about the hardware-backed keystore feature, -see the guide for <a href="https://source.android.com/security/keystore/" -class="external-link">Hardware-backed Keystore</a>.</p> +<p> +  Key attestation allows you to verify that an RSA or EC key pair has been +  created and stored in a device’s hardware-backed keystore within the device’s +  trusted execution environment (TEE). The tool also allows you to use an +  off-device service, such as your app's back-end server, to determine and +  strongly verify the uses and validity of the key pair. These features provide +  an additional level of security that protects the key pair, even if someone +  roots the device or compromises the security of the Android platform running +  on the device. +</p> -<p>In addition to Key Attestation, Android N also introduces -  fingerprint-bound keys that are not revoked on fingerprint enrollment.</p> +<p> +  For more information, see the +  <a href="{@docRoot}preview/features/key-attestation.html">Key Attestation</a> +  developer documentation. +</p>  <h2 id="network_security_config">Network Security Config</h2> @@ -854,18 +843,26 @@ Directory Access</a> developer documentation.</p>  <h2 id="keyboard_shortcuts_helper">Keyboard Shortcuts Helper</h2>  <p> -In Android N, the user can press "Alt + /" to trigger a <em>Keyboard Shortcuts</em> -screen that displays all shortcuts available both from the system and from -the app in focus. These are retrieved automatically from the app’s menu if -available, but developers can provide their own fine-tuned shortcuts lists -for the screen. You can do this by overriding the new -<code>Activity.onProvideKeyboardShortcuts()</code> method, described in the downloadable -<a href="{@docRoot}preview/setup-sdk.html#docs-dl">API Reference</a>. +  In Android N, the user can press <strong>Meta + /</strong> to trigger a +  <em>Keyboard Shortcuts</em> screen that displays all shortcuts available both +  from the system and from the app in focus. The system retrieves these +  shortcuts automatically from the app’s menu if the shortcuts exist. You can +  also provide your own fine-tuned shortcuts lists for the screen. You can do +  this by overriding the new <code>Activity.onProvideKeyboardShortcuts()</code> +  method, described in the downloadable <a href= +  "{@docRoot}preview/setup-sdk.html#docs-dl">API Reference</a>. +</p> + +<p class="note"> +  <strong>Note:</strong> The <strong>Meta</strong> key is not present on all +  keyboards: on a Macintosh keyboard, it is the <strong>Command</strong> key, +  on the Windows keyboard, it is the <strong>Windows</strong> key, and on the +  Pixel C and the Chrome OS keyboards, it is the <strong>Search</strong> key.  </p>  <p> -To trigger the Keyboard Shortcuts Helper from anywhere in your app, -call {@code Activity.requestKeyboardShortcutsHelper()} for the relevant activity. +  To trigger Keyboard Shortcuts Helper from anywhere in your app, call +  {@code Activity.requestKeyboardShortcutsHelper()} for the relevant activity.  </p>  <h2 id="sustained_performance_api">Sustained Performance API</h2> diff --git a/docs/html/preview/download-ota.jd b/docs/html/preview/download-ota.jd index 2058501dd9a9..18f3e8d01f0a 100644 --- a/docs/html/preview/download-ota.jd +++ b/docs/html/preview/download-ota.jd @@ -203,72 +203,72 @@ This is the Android SDK Preview License Agreement (the “License Agreementâ€).    <tr id="bullhead">      <td>Nexus 5X <br>"bullhead"</td>      <td><a href="#top" onclick="onDownload(this)" -      >bullhead-ota-npd35k-b8cfbd80.zip</a><br> -      MD5: 15fe2eba9b01737374196bdf0a792fe9<br> -      SHA-1: 5014b2bba77f9e1a680ac3f90729621c85a14283 +      >bullhead-ota-npd56n-dd5c12ee.zip</a><br> +      MD5: af9a82e9a78925ca9c1c7f5f6fb851ec<br> +      SHA-1: e4aabd5634b7ebdeffa877cd9e49244c0be326e4      </td>    </tr>    <tr id="shamu">      <td>Nexus 6 <br>"shamu"</td>      <td><a href="#top" onclick="onDownload(this)" -      >shamu-ota-npd35k-078e6fa5.zip</a><br> -      MD5: e8b12f7721c53af9a450f7058928a5fc<br> -      SHA-1: b7a9b756f84a1d2e482ff9c16749d65f6e51425a +      >shamu-ota-npd56n-2818fd62.zip</a><br> +      MD5: d8df396b187a8667889260e5464bd676<br> +      SHA-1: c03c8ef8be587a574565855d4faa526254794e03      </td>    </tr>    <tr id="angler">      <td>Nexus 6P <br>"angler"</td>      <td><a href="#top" onclick="onDownload(this)" -      >angler-ota-npd35k-88457699.zip</a><br> -      MD5: 3fac09fef759dde26e57cb80b20b6477<br> -      SHA-1: 27d6caa786577d8a38b2da5bf94b33b4524a1a1c +      >angler-ota-npd56n-d2f2611c.zip</a><br> +      MD5: c3c206892d414d4fc7da892ff840eada<br> +      SHA-1: 2bdc79409ace5e163ef014ae51977d0a71b83df5      </td>    </tr>    <tr id="volantis">      <td>Nexus 9 <br>"volantis"</td>      <td><a href="#top" onclick="onDownload(this)" -      >volantis-ota-npd35k-51dbae76.zip</a><br> -      MD5: 58312c4a5971818ef5c77a3f446003da<br> -      SHA-1: aad9005be33d3e2bab480509a6ab74c3c3b9d921 +      >volantis-ota-npd56n-42228a60.zip</a><br> +      MD5: c80cf483d8b3c014fc7b27f80957a158<br> +      SHA-1: f437829320f47ea3aa5f8b70ce2f0bb3d30b3f4f      </td>    </tr>    <tr id="volantisg">      <td>Nexus 9G <br>"volantisg"</td>      <td><a href="#top" onclick="onDownload(this)" -      >volantisg-ota-npd35k-834f047f.zip</a><br> -      MD5: 92b7d1fa252f7394e70f957c72d4aac8<br> -      SHA-1: b6c057c84d90893630e303cbb60530e20ddb8361 +      >volantisg-ota-npd56n-9b4dbaac.zip</a><br> +      MD5: 9e55ac1650e4f07a662bafa7f082e91c<br> +      SHA-1: b9982be56c2817d122664869a1fbe9b13e9c72f7      </td>    </tr>    <tr id="fugu">      <td>Nexus Player <br>"fugu"</td>      <td><a href="#top" onclick="onDownload(this)" -      >fugu-ota-npd35k-6ac91298.zip</a><br> -      MD5: 1461622ad53ea842b2722fa7b49b8172<br> -      SHA-1: 409c061668ab270774877d7f3eae44fa48d2b931 +      >fugu-ota-npd56n-b305968a.zip</a><br> +      MD5: dfc980acad6772d8473ccaa9cbbb681a<br> +      SHA-1: d7bf8192649dea970afda165d181b4eea07abd7d      </td>    </tr>    <tr id="ryu">      <td>Pixel C <br>"ryu"</td>      <td><a href="#top" onclick="onDownload(this)" -      >ryu-ota-npd35k-a0b2347f.zip</a><br> -      MD5: c60117f3640cc6db12386fd632289c7d<br> -      SHA-1: 87349c767c69efb4172c90ce1d88cf578c3d28b3 +      >ryu-ota-npd56n-5bf2fd66.zip</a><br> +      MD5: 1699e4bacfbef16a75ae6cf3f2e3d886<br> +      SHA-1: e20f3a8e43fcdd6acef21da80894afc8f9474e33      </td>    </tr>    <tr id="seed">      <td>General Mobile 4G (Android One) <br>"seed"</td>      <td><a href="#top" onclick="onDownload(this)" -      >seed_l8150-ota-npd35k-09897a1d.zip</a><br> -      MD5: a55cf94f7cce0393ec6c0b35041766b7<br> -      SHA-1: 6f33742290eb46f2561891f38ca2e754b4e50c6a +      >seed_l8150-ota-npd56n-a322696c.zip</a><br> +      MD5: afc0e363ad2fd7418423e189a339a8e9<br> +      SHA-1: fc4d818878df51894eac29932dd0e9f6511329c6      </td>    </tr> diff --git a/docs/html/preview/download.jd b/docs/html/preview/download.jd index b5405c7c934d..ad82211d0e65 100644 --- a/docs/html/preview/download.jd +++ b/docs/html/preview/download.jd @@ -302,72 +302,72 @@ This is the Android SDK Preview License Agreement (the “License Agreementâ€).    <tr id="bullhead">      <td>Nexus 5X <br>"bullhead"</td>      <td><a href="#top" onclick="onDownload(this)" -      >bullhead-npd35k-factory-5ba40535.tgz</a><br> -      MD5: b6c5d79a21815ee21db41822dcf61e9f<br> -      SHA-1: 5ba4053577007d15c96472206e3a79bc80ab194c +      >bullhead-npd56n-factory-996cac57.tgz</a><br> +      MD5: 5aadba91f60de00d58dc6198ef5cc3ba<br> +      SHA-1: 996cac575d83bde573315290da8f52cecc4127d2      </td>    </tr>    <tr id="shamu">      <td>Nexus 6 <br>"shamu"</td>      <td><a href="#top" onclick="onDownload(this)" -      >shamu-npd35k-factory-a33bf20c.tgz</a><br> -      MD5: e1cf9c57cfb11bebe7f1f5bfbf05d7ab<br> -      SHA-1: a33bf20c719206bcf08d1edd8da6c0ff9d50f69c +      >shamu-npd56n-factory-7936bf75.tgz</a><br> +      MD5: b7ed0db569f3bc2d6655fe8d8cea0e13<br> +      SHA-1: 7936bf75e6bfb771bd14485211a319b246311b96      </td>    </tr>    <tr id="angler">      <td>Nexus 6P <br>"angler"</td>      <td><a href="#top" onclick="onDownload(this)" -      >angler-npd35k-factory-81c341d5.tgz</a><br> -      MD5: e93de7949433339856124c3729c15ebb<br> -      SHA-1: 81c341d57ef2cd139569b055d5d59e9e592a7abd +      >angler-npd56n-factory-1ce5ccad.tgz</a><br> +      MD5: f296eccaed4e2526d6435df8cf0e8df1<br> +      SHA-1: 1ce5ccad8a3eae143e0ecd9c7afbb1be2f1d41cc      </td>    </tr>    <tr id="volantis">      <td>Nexus 9 <br>"volantis"</td>      <td><a href="#top" onclick="onDownload(this)" -      >volantis-npd35k-factory-2b50e19d.tgz</a><br> -      MD5: 565be87ebb2d5937e2abe1a42645864b<br> -      SHA-1: 2b50e19dae2667b27f911e3c61ed64860caf43e1 +      >volantis-npd56n-factory-8b9f997e.tgz</a><br> +      MD5: 111c2fe5777dd6aae71fb8ef35dda9d3<br> +      SHA-1: 8b9f997ea39fdaf505527536bd346948ae1bae30      </td>    </tr>    <tr id="volantisg">      <td>Nexus 9G <br>"volantisg"</td>      <td><a href="#top" onclick="onDownload(this)" -      >volantisg-npd35k-factory-2e89ebe6.tgz</a><br> -      MD5: a8464e15c6683fe2afa378a63e205fda<br> -      SHA-1: 2e89ebe67a46b2f3beb050746c13341cd11fa678 +      >volantisg-npd56n-factory-ef05106a.tgz</a><br> +      MD5: 3a6f4d47b385966347bd26b7a922cd6e<br> +      SHA-1: ef05106a9e3becea5673ea67d6c0cc21a2ec09d4      </td>    </tr>    <tr id="fugu">      <td>Nexus Player <br>"fugu"</td>      <td><a href="#top" onclick="onDownload(this)" -      >fugu-npd35k-factory-1de74874.tgz</a><br> -      MD5: c0dbb7db671f61b2785da5001cedefcb<br> -      SHA-1: 1de74874f8d83e14d642f13b5a2130fc2aa55873 +      >fugu-npd56n-factory-a51674a1.tgz</a><br> +      MD5: b75dc745a64848ea24124db8fa9252ed<br> +      SHA-1: a51674a1303b17fec0405d513f9c0fe9f225780f      </td>    </tr>    <tr id="ryu">      <td>Pixel C <br>"ryu"</td>      <td><a href="#top" onclick="onDownload(this)" -      >ryu-npd35k-factory-b4eed85d.tgz</a><br> -      MD5: bdcb6f770e753668b5fadff2a6678e0d<br> -      SHA-1: b4eed85de0d42c200348a8629084f78e24f72ac2 +      >ryu-npd56n-factory-e36c49b1.tgz</a><br> +      MD5: 0a2d660b09e19614a5b3573487b88066<br> +      SHA-1: e36c49b184843cdfe10278aebc04ce50b6d670b6      </td>    </tr>    <tr id="seed">      <td>General Mobile 4G (Android One) <br>"seed"</td>      <td><a href="#top" onclick="onDownload(this)" -      >seed_l8150-npd35k-factory-5ab1212b.tgz</a><br> -      MD5: 7d34a9774fdd6e025d485ce6cfc23c4c<br> -      SHA-1: 5ab1212bc9417269d391aacf1e672fff24b4ecc5 +      >seed_l8150-npd56n-factory-dd5d4fd2.tgz</a><br> +      MD5: 3420581b969af777753141dacc7f73b9<br> +      SHA-1: dd5d4fd203f9c5dad658434c0ff370c411b78835      </td>    </tr> diff --git a/docs/html/preview/features/direct-boot.jd b/docs/html/preview/features/direct-boot.jd index 3d442d0966ca..8351f4b4b10f 100644 --- a/docs/html/preview/features/direct-boot.jd +++ b/docs/html/preview/features/direct-boot.jd @@ -108,18 +108,25 @@ Direct Boot mode, use credential encrypted storage.</p>  <h2 id="notification">Getting Notified of User Unlock</h2> -<p>Once the user unlocks the device after restart, your app can switch to +<p>When the user unlocks the device after restart, your app can switch to  accessing credential encrypted storage and use regular system services that  depend on user credentials.</p>  <p>To get notified when the user unlocks the device after a reboot,  register a {@link android.content.BroadcastReceiver} from a running component -to listen for the <code>ACTION_USER_UNLOCKED</code> message. Or, you can -receive the existing {@link android.content.Intent#ACTION_BOOT_COMPLETED -ACTION_BOOT_COMPLETED} message, which now indicates the device has booted and -the user has unlocked the device.</p> +to listen for unlock notification messages. When the user unlocks the device +after boot: +</p> +<ul> +<li>If your app has foreground processes that need immediate notification, +listen for the {@code ACTION_USER_UNLOCKED} message.</li> +<li>If your app only uses background processes that can act on a delayed +notification, listen for the +{@link android.content.Intent#ACTION_BOOT_COMPLETED ACTION_BOOT_COMPLETED} +message.</li> +</ul> -<p>You can directly query if the user has unlocked the device by calling +<p>If the user has unlocked the device, you can find out by calling  <code>UserManager.isUserUnlocked()</code>.</p>  <h2 id="migrating">Migrating Existing Data</h2> diff --git a/docs/html/preview/features/key-attestation.jd b/docs/html/preview/features/key-attestation.jd new file mode 100644 index 000000000000..98b8340496d2 --- /dev/null +++ b/docs/html/preview/features/key-attestation.jd @@ -0,0 +1,845 @@ +page.title=Key Attestation +page.metaDescription=New support in Android N for verifying security properties of hardware-backed keys. +page.keywords="android N", "security", "TEE", "hardware-backed", "keystore", "certificate", "key attestation" + +@jd:body + +<div id="qv-wrapper"> +  <div id="qv"> +    <h2>In this document</h2> +      <ol> +        <li><a href="#verifying">Retrieving and Verifying a Hardware-backed Key Pair</a></li> +        <li><a href="#certificate_schema">Certificate Extension Data Schema</a></li> +      </ol> +  </div> +</div> + +<p> +  Key Attestation gives you more confidence that the keys you use in your app +  are stored in a device's hardware-backed keystore. The following sections +  describe how to verify the properties of hardware-backed keys and how to +  interpret the schema of the attestation certificate's extension data. +</p> + +<h2 id="verifying"> +  Retrieving and Verifying a Hardware-backed Key Pair +</h2> + +<p> +  During key attestation, you specify the alias of a key pair. The attestation +  tool, in return, provides a certificate chain, which you can use to verify +  the properties of that key pair. +</p> + +<p> +  The root certificate within this chain is signed using an attestation key, +  which the device manufacturer injects into the device’s hardware-backed +  keystore at the factory. +</p> + +<p class="note"> +  <strong>Note:</strong> On devices that ship with Android N and Google Play +  services, the root certificate is issued by Google. You should verify that +  this root certificate appears within Google’s list of root certificates. +</p> + +<p> +  To implement key attestation, complete the following steps: +</p> + +<ol> +  <li> +    Use a {@link java.security.KeyStore KeyStore} object's +    {@link java.security.KeyStore#getCertificateChain getCertificateChain()} +    method to get a reference to the chain of X.509 certificates associated with +    the hardware-backed keystore. +  </li> + +  <li> +    <p> +      Check each certificate’s validity using a +      {@link java.security.cert.CRL CRL} object's +      {@link java.security.cert.CRL#isRevoked isRevoked()} method. +    </p> + +    <p class="caution"> +      <strong>Caution:</strong> Although you can complete this process within +      your app directly, it’s safer to check the certificates’ revocation lists +      on a separate server that you trust. +    </p> +  </li> + +  <li> +    <p> +      Create an <code>Attestation</code> object, passing in the first element of +      the certificate chain as an argument:</p> + +<pre> +// "certificates" contains the certificate chain associated with a specific key +// pair in the device's hardware-backed keystore. +X509Certificate attestationCert = (X509Certificate) certificates[0]; +Attestation hardwareKeyAttestation = new Attestation(attestationCert); +</pre> + +    <p> +      An attestation object extracts the extension data within this certificate +      and stores this information in a more accessible format. For more details +      about the schema of the extension data, see <a href= +      "#certificate_schema">Certificate Extension Data Schema</a>. +    </p> +  </li> + +  <li> +    <p> +      Use the accessor methods within the <code>Attestation</code> class to +      retrieve the extension data from the certificate. These methods use the +      same names and structure hierarchy as in the certificate extension data +      schema. +    </p> + +    <p> +      For example, to view the verified boot key for the device’s TEE, use the +      following method sequence: +    </p> + +<pre> +// "hardwareKeyAttestation" contains the first element of the attestation +// certificate chain. +AuthorizationList teeAuthList = hardwareKeyAttestation.getTeeEnforced(); +RootOfTrust teeRootOfTrust = teeAuthList.getRootOfTrust(); +byte[] teeVerifiedBootKey = teeRootOfTrust.getVerifiedBootKey(); +</pre> + +  </li> + +  <li> +    <p> +      Compare the extension data from the <code>Attestation</code> object with +      the set of values that you expect the hardware-backed key to contain. +    </p> + +    <p class="caution"> +      <strong>Caution:</strong> Although you can complete this process within +      your app directly, it’s safer to check the certificate’s extension data +      on a separate server that you trust. +    </p> +  </li> +</ol> + +<h2 id="certificate_schema"> +  Certificate Extension Data Schema +</h2> + +<p> +  Key attestation verifies the extension data that appears in the first +  certificate within the chain in a device’s hardware-backed keystore. The +  certificate stores the information according to the following ASN.1 schema: +</p> + +<pre class="no-pretty-print"> +KeyDescription ::= SEQUENCE { +    attestationVersion  INTEGER, +    attestationSecurityLevel  SecurityLevel, +    keymasterVersion  INTEGER, +    keymasterSecurityLevel  SecurityLevel, +    attestationChallenge  OCTET_STRING, +    <var>reserved  OCTET_STRING</var>, +    softwareEnforced  AuthorizationList, +    teeEnforced  AuthorizationList, +} + +SecurityLevel ::= ENUMERATED { +    Software  (0), +    TrustedEnvironment  (1), +} + +AuthorizationList ::= SEQUENCE { +    purpose  [1] EXPLICIT SET OF INTEGER OPTIONAL, +    algorithm  [2] EXPLICIT INTEGER OPTIONAL, +    keySize  [3] EXPLICIT INTEGER OPTIONAL, +    digest  [5] EXPLICIT SET OF INTEGER OPTIONAL, +    padding  [6] EXPLICIT SET OF INTEGER OPTIONAL, +    ecCurve  [10] EXPLICIT INTEGER OPTIONAL, +    rsaPublicExponent  [200] EXPLICIT INTEGER OPTIONAL, +    activeDateTime  [400] EXPLICIT INTEGER OPTIONAL, +    originationExpireDateTime  [401] EXPLICIT INTEGER OPTIONAL, +    usageExpireDateTime  [402] EXPLICIT INTEGER OPTIONAL, +    noAuthRequired  [503] EXPLICIT NULL OPTIONAL, +    userAuthType  [504] EXPLICIT INTEGER OPTIONAL, +    authTimeout  [505] EXPLICIT INTEGER OPTIONAL, +    allowWhileOnBody  [506] EXPLICIT NULL OPTIONAL, +    allApplications  [600] EXPLICIT NULL OPTIONAL, +    applicationId  [601] EXPLICIT OCTET_STRING OPTIONAL, +    creationDateTime  [701] EXPLICIT INTEGER OPTIONAL, +    origin  [702] EXPLICIT INTEGER OPTIONAL, +    rollbackResistant  [703] EXPLICIT NULL OPTIONAL, +    rootOfTrust  [704] EXPLICIT RootOfTrust OPTIONAL, +    osVersion  [705] EXPLICIT INTEGER OPTIONAL, +    osPatchLevel  [706] EXPLICIT INTEGER OPTIONAL, +    attestationChallenge  [708] EXPLICIT INTEGER OPTIONAL, +    attestationApplicationId  [709] EXPLICIT OCTET_STRING OPTIONAL, +} + +RootOfTrust ::= SEQUENCE { +    verifiedBootKey  OCTET_STRING, +    deviceLocked  BOOLEAN, +    verifiedBootState  VerifiedBootState, +} + +VerifiedBootState ::= ENUMERATED { +    Verified  (0), +    SelfSigned  (1), +    Unverified  (2), +    Failed  (3), +} +</pre> + +<p> +  The following list presents a description of each element within the schema: +</p> + +<h3 id="certificate_schema_keydescription"> +  KeyDescription +</h3> + +<p> +  This sequence of values presents general information about the key pair being +  verified through key attestation and provides easy access to additional +  details. +</p> + +<dl> +  <dt> +    <code>attestationVersion</code> +  </dt> + +  <dd> +    The version of the key attestation feature. Should be set to 1. +  </dd> + +  <dt> +    <code>attestationSecurity</code> +  </dt> + +  <dd> +    <p> +      The <a href="#certificate_schema_securitylevel">security +      level</a> of the attestation. +    </p> + +    <p class="note"> +      <strong>Note:</strong> Although it is possible to attest keys that are +      stored in the Android system—that is, if the +      <code>attestationSecurity</code> value is set to Software—you +      cannot trust these attestations if the Android system becomes compromised. +    </p> +  </dd> + +  <dt> +    <code>keymasterVersion</code> +  </dt> + +  <dd> +    The version of the Keymaster hardware abstraction layer (HAL). Use 0 to +    represent version 0.2 or 0.3, 1 to represent version 1.0, and 2 to represent +    version 2.0. +  </dd> + +  <dt> +    <code>keymasterSecurity</code> +  </dt> + +  <dd> +    The <a href="#certificate_schema_securitylevel">security +    level</a> of the Keymaster implementation. +  </dd> + +  <dt> +    <code>attestationChallenge</code> +  </dt> + +  <dd> +    The challenge string associated with a key pair that is verified using key +    attestation. +  </dd> + +  <dt> +    <code><var>reserved</var></code> +  </dt> + +  <dd> +    Only system apps use this value. In all other apps, this value is empty. +  </dd> + +  <dt> +    <code>softwareEnforced</code> +  </dt> + +  <dd> +    Optional. The Keymaster <a href= +    "#certificate_schema_authorizationlist">authorization +    list</a> that is enforced by the Android system, not by the device’s TEE. +  </dd> + +  <dt> +    <code>teeEnforced</code> +  </dt> + +  <dd> +    Optional. The Keymaster <a href= +    "#certificate_schema_authorizationlist">authorization +    list</a> that is enforced by the device’s TEE. +  </dd> +</dl> + +<h3 id="certificate_schema_securitylevel"> +  SecurityLevel +</h3> + +<p> +  This data structure indicates the extent to which a software feature, such as +  a key pair, is protected based on its location within the device. +</p> + +<p> +  Because the data structure is an enumeration, it takes on exactly one of the +  following values: +</p> + +<dl> +  <dt> +    Software +  </dt> + +  <dd> +    The logic for creating and managing the feature is implemented in the +    Android system. For the purposes of creating and storing key pairs, this +    location is less secure than the TEE but is more secure than your app's +    process space. +  </dd> + +  <dt> +    TrustedEnvironment +  </dt> + +  <dd> +    The logic for creating and managing the feature is implemented in secure +    hardware, such as a TEE. For the purposes of creating and storing key pairs, +    this location is more secure because secure hardware is highly resistant to +    remote compromise. +  </dd> +</dl> + +<h3 id="certificate_schema_authorizationlist"> +  AuthorizationList +</h3> + +<p> +  This data structure contains the key pair’s properties themselves, as defined +  in the Keymaster hardware abstraction layer (HAL). You compare these values +  to the device’s current state or to a set of expected values to verify that a +  key pair is still valid for use in your app. +</p> + +<p> +  Each field name corresponds to a similarly-named Keymaster tag. For example, +  the <code>keySize</code> field in an authorization list corresponds to the +  <code>KM_TAG_KEY_SIZE</code> Keymaster tag. +</p> + +<p> +  Each field in the following list is optional: +</p> + +<dl> +  <dt> +    <code>purpose</code> +  </dt> + +  <dd> +    Corresponds to the <code><a href= +    "https://source.android.com/security/keystore/implementer-ref.html#km_tag_purpose"> +    KM_TAG_PURPOSE</a></code> Keymaster tag, which uses a tag ID value of 1. +  </dd> + +  <dt> +    <code>algorithm</code> +  </dt> + +  <dd> +    <p> +      Corresponds to the <code><a href= +      "https://source.android.com/security/keystore/implementer-ref.html#km_tag_algorithm"> +      KM_TAG_ALGORITHM</a></code> Keymaster tag, which uses a tag ID value of +      2. +    </p> + +    <p> +      When an <code>AuthorizationList</code> object is associated with key +      attestation, this value is always <code>KM_ALGORITHM_RSA</code> or +      <code>KM_ALGORITHM_EC</code>. +    </p> +  </dd> + +  <dt> +    <code>keySize</code> +  </dt> + +  <dd> +    Corresponds to the <code><a href= +    "https://source.android.com/security/keystore/implementer-ref.html#km_tag_key_size"> +    KM_TAG_KEY_SIZE</a></code> Keymaster tag, which uses a tag ID value of 3. +  </dd> + +  <dt> +    <code>digest</code> +  </dt> + +  <dd> +    Corresponds to the <code><a href= +    "https://source.android.com/security/keystore/implementer-ref.html#km_tag_digest"> +    KM_TAG_DIGEST</a></code> Keymaster tag, which uses a tag ID value of 5. +  </dd> + +  <dt> +    <code>padding</code> +  </dt> + +  <dd> +    Corresponds to the <code><a href= +    "https://source.android.com/security/keystore/implementer-ref.html#km_tag_padding"> +    KM_TAG_PADDING</a></code> Keymaster tag, which uses a tag ID value of 6. +  </dd> + +  <dt> +    <code>ecCurve</code> +  </dt> + +  <dd> +    <p> +      Corresponds to the <code>KM_TAG_EC_CURVE</code> Keymaster tag, which uses +      a tag ID value of 10. +    </p> + +    <p> +      The set of parameters used to generate an elliptic curve (EC) key pair, +      which uses ECDSA for signing and verification, within the Android system +      keystore. +    </p> +  </dd> + +  <dt> +    <code>rsaPublicExponent</code> +  </dt> + +  <dd> +    Corresponds to the <code><a href= +    "https://source.android.com/security/keystore/implementer-ref.html#km_tag_rsa_public_exponent"> +    KM_TAG_RSA_PUBLIC_EXPONENT</a></code> Keymaster tag, which uses a tag ID +    value of 200. +  </dd> + +  <dt> +    <code>activeDateTime</code> +  </dt> + +  <dd> +    Corresponds to the <code><a href= +    "https://source.android.com/security/keystore/implementer-ref.html#km_tag_active_datetime"> +    KM_TAG_ACTIVE_DATETIME</a></code> Keymaster tag, which uses a tag ID value +    of 400. +  </dd> + +  <dt> +    <code>originationExpireDateTime</code> +  </dt> + +  <dd> +    Corresponds to the <code><a href= +    "https://source.android.com/security/keystore/implementer-ref.html#km_tag_origination_expire_datetime"> +    KM_TAG_ORIGINATION_EXPIRE_DATETIME</a></code> Keymaster tag, which uses a +    tag ID value of 401. +  </dd> + +  <dt> +    <code>usageExpireDateTime</code> +  </dt> + +  <dd> +    Corresponds to the <code><a href= +    "https://source.android.com/security/keystore/implementer-ref.html#km_tag_usage_expire_datetime"> +    KM_TAG_USAGE_EXPIRE_DATETIME</a></code> Keymaster tag, which uses a tag ID +    value of 402. +  </dd> + +  <dt> +    <code>noAuthRequired</code> +  </dt> + +  <dd> +    <p> +      Corresponds to the <code><a href= +      "https://source.android.com/security/keystore/implementer-ref.html#km_tag_no_auth_required"> +      KM_TAG_NO_AUTH_REQUIRED</a></code> Keymaster tag, which uses a tag ID +      value of 503. +    </p> + +    <p> +      When an <code>AuthorizationList</code> object is associated with key +      attestation, this value is always true. +    </p> +  </dd> + +  <dt> +    <code>userAuthType</code> +  </dt> + +  <dd> +    Corresponds to the <code><a href= +    "https://source.android.com/security/keystore/implementer-ref.html#km_tag_user_auth_type"> +    KM_TAG_USER_AUTH_TYPE</a></code> Keymaster tag, which uses a tag ID value +    of 504. +  </dd> + +  <dt> +    <code>authTimeout</code> +  </dt> + +  <dd> +    Corresponds to the <code><a href= +    "https://source.android.com/security/keystore/implementer-ref.html#km_tag_auth_timeout"> +    KM_TAG_AUTH_TIMEOUT</a></code> Keymaster tag, which uses a tag ID value of +    505. +  </dd> + +  <dt> +    <code>allowWhileOnBody</code> +  </dt> + +  <dd> +    <p> +      Corresponds to the <code>KM_TAG_ALLOW_WHILE_ON_BODY</code> Keymaster tag, +      which uses a tag ID value of 506. +    </p> + +    <p> +      Allows the key to be used after its authentication timeout period if the +      user is still wearing the device on their body. Note that a secure +      on-body sensor determines whether the device is being worn on the user’s +      body. +    </p> + +    <p> +      When an <code>AuthorizationList</code> object is associated with key +      attestation, this value is always true. +    </p> +  </dd> + +  <dt> +    <code>allApplications</code> +  </dt> + +  <dd> +    <p> +      Corresponds to the <code>KM_TAG_ALL_APPLICATIONS</code> Keymaster tag, +      which uses a tag ID value of 600. +    </p> + +    <p> +      Indicates whether all apps on a device can access the key pair. +    </p> + +    <p> +      When an <code>AuthorizationList</code> object is associated with key +      attestation, this value is always true. +    </p> +  </dd> + +  <dt> +    <code>applicationId</code> +  </dt> + +  <dd> +    Corresponds to the <code><a href= +    "https://source.android.com/security/keystore/implementer-ref.html#km_tag_application_id"> +    KM_TAG_APPLICATION_ID</a></code> Keymaster tag, which uses a tag ID value +    of 601. +  </dd> + +  <dt> +    <code>creationDateTime</code> +  </dt> + +  <dd> +    Corresponds to the <code><a href= +    "https://source.android.com/security/keystore/implementer-ref.html#km_tag_creation_datetime"> +    KM_TAG_CREATION_DATETIME</a></code> Keymaster tag, which uses a tag ID +    value of 701. +  </dd> + +  <dt> +    <code>origin</code> +  </dt> + +  <dd> +    <p> +      Corresponds to the <code><a href= +      "https://source.android.com/security/keystore/implementer-ref.html#km_tag_origin"> +      KM_TAG_ORIGIN</a></code> Keymaster tag, which uses a tag ID value of 702. +    </p> + +    <p> +      When an <code>AuthorizationList</code> object is associated with key +      attestation, this value is usually set to +      <code>KM_ORIGIN_GENERATED</code>. If the attestation uses Keymaster +      version 0.2 or 0.3, however, the origin may be set to +      <code>KM_ORIGIN_UNKNOWN</code> instead. +    </p> +  </dd> + +  <dt> +    <code>rollbackResistant</code> +  </dt> + +  <dd> +    Corresponds to the <code><a href= +    "https://source.android.com/security/keystore/implementer-ref.html#km_tag_rollback_resistant"> +    KM_TAG_ROLLBACK_RESISTANT</a></code> Keymaster tag, which uses a tag ID +    value of 703. +  </dd> + +  <dt> +    <code>rootOfTrust</code> +  </dt> + +  <dd> +    <p> +      Corresponds to the <code><a href= +      "https://source.android.com/security/keystore/implementer-ref.html#km_tag_root_of_trust"> +      KM_TAG_ROOT_OF_TRUST</a></code> Keymaster tag, which uses a tag ID value +      of 704. +    </p> + +    <p> +      For more details, see the section describing the <code><a href= +      "#certificate_schema_rootoftrust">RootOfTrust</a></code> +      data structure. +    </p> +  </dd> + +  <dt> +    <code>osVersion</code> +  </dt> + +  <dd> +    <p> +      Corresponds to the <code>KM_TAG_OS_VERSION</code> Keymaster tag, which +      uses a tag ID value of 705. +    </p> + +    <p> +      The version of the Android operating system associated with the +      Keymaster, specified as a six-digit integer. For example, version 6.0.1 +      is represented as 060001. +    </p> + +    <p> +      Only Keymaster version 1.0 or higher includes this value in the +      authorization list. +    </p> +  </dd> + +  <dt> +    <code>osPatchLevel</code> +  </dt> + +  <dd> +    <p> +      Corresponds to the <code>KM_TAG_PATCHLEVEL</code> Keymaster tag, which +      uses a tag ID value of 706. +    </p> + +    <p> +      The month and year associated with the security patch that is being used +      within the Keymaster, specified as a six-digit integer. For example, the +      June 2016 patch is represented as 201606. +    </p> + +    <p> +      Only Keymaster version 1.0 or higher includes this value in the +      authorization list. +    </p> +  </dd> + +  <dt> +    <code>attestationChallenge</code> +  </dt> + +  <dd> +    <p> +      Corresponds to the <code>KM_TAG_ATTESTATION_CHALLENGE</code> Keymaster +      tag, which uses a tag ID value of 708. +    </p> + +    <p> +      The challenge string associated with the key pair that is defined in the +      Keymaster. +    </p> +  </dd> + +  <dt> +    <code>attestationApplicationId</code> +  </dt> + +  <dd> +    <p> +      Corresponds to the <code>KM_TAG_ATTESTATION_APPLICATION_ID</code> +      Keymaster tag, which uses a tag ID value of 709. +    </p> + +    <p> +      The unique ID of the attestation certificate that signed the key pair +      that is in the Keymaster. +    </p> +  </dd> +</dl> + +<h3 id="certificate_schema_rootoftrust"> +  RootOfTrust +</h3> + +<p> +  This collection of values defines key information about the device’s status. +</p> + +<p> +  Each field in the following list is required: +</p> + +<dl> +  <dt> +    <code>verifiedBootKey</code> +  </dt> + +  <dd> +    <p> +      A secure hash of the key that verifies the system image. It is recommended +      that you use the SHA-256 algorithm for this hash. +    </p> +  </dd> + +  <dt> +    <code>deviceLocked</code> +  </dt> + +  <dd> +    True if the device’s bootloader is locked, which enables Verified Boot +    checking and prevents an unsigned device image from being flashed onto the +    device. For more information about this feature, see the <a class= +    "external-link" href= +    "https://source.android.com/security/verifiedboot/verified-boot.html">Verifying +    Boot</a> documentation. +  </dd> + +  <dt> +    <code>verifiedBootState</code> +  </dt> + +  <dd> +    The <a href="#certificate_schema_verifiedbootstate">boot +    state</a> of the device, according to the Verified Boot feature. +  </dd> + +  <dt> +    <code>osVersion</code> +  </dt> + +  <dd> +    The current version of the Android operating system on the device, +    specified as a six-digit integer. For example, version 6.0.1 is represented +    as 060001. +  </dd> + +  <dt> +    <code>patchMonthYear</code> +  </dt> + +  <dd> +    The month and year associated with the security patch that is currently +    installed on the device, specified as a six-digit integer. For example, the +    June 2016 patch is represented as 201606. +  </dd> +</dl> + +<h3 id="certificate_schema_verifiedbootstate"> +  VerifiedBootState +</h3> + +<p> +  This data structure provides the device’s current boot state, which +  represents the level of protection provided to the user and to apps after the +  device finishes booting. For more information about this feature, see the +  <a class="external-link" href= +  "https://source.android.com/security/verifiedboot/verified-boot.html#boot_state"> +  Boot State</a> section within the Verifying Boot documentation. +</p> + +<p> +  This data structure is an enumeration, so it takes on exactly one of the +  following values: +</p> + +<dl> +  <dt> +    Verified +  </dt> + +  <dd> +    <p> +      Indicates a full chain of trust, which includes the bootloader, the boot +      partition, and all verified partitions. +    </p> + +    <p> +      When the device is in this boot state, the <code>verifiedBootKey</code> is +      the hash of the device-embedded certificate, which the device manufacturer +      adds to the device's ROM at the factory. +    </p> +  </dd> + +  <dt> +    SelfSigned +  </dt> + +  <dd> +    <p> +      Indicates that the device-embedded certificate has verified the device’s +      boot partition and that the signature is valid. +    </p> + +    <p> +      When the device is in this boot state, the <code>verifiedBootKey</code> is +      the hash of a user-installed certificate, which signs a boot partition +      that the user adds to the device in place of the original, +      manufacturer-provided boot partition. +    </p> +  </dd> + +  <dt> +    Unverified +  </dt> + +  <dd> +    Indicates that the user can modify the device freely. Therefore, the user is +    responsible for verifying the device’s integrity. +  </dd> + +  <dt> +    Failed +  </dt> + +  <dd> +    Indicates that the device has failed verification. The attestation +    certificate should never use this value for <code>VerifiedBootState</code>. +  </dd> +</dl> diff --git a/docs/html/preview/features/multi-window.jd b/docs/html/preview/features/multi-window.jd index a4f389a0948b..ca5bd0da292e 100644 --- a/docs/html/preview/features/multi-window.jd +++ b/docs/html/preview/features/multi-window.jd @@ -471,7 +471,7 @@ android:supportsPictureInPicture=["true" | "false"]    </dd>    <dt> -    <code>Activity.requestDropPermissions()</code> +    <code>Activity.requestDragAndDropPermissions()</code>    </dt>    <dd> diff --git a/docs/html/preview/index.jd b/docs/html/preview/index.jd index 918de48c41d3..0b21e68cd855 100644 --- a/docs/html/preview/index.jd +++ b/docs/html/preview/index.jd @@ -114,7 +114,24 @@ footer.hide=1      data-initial-results="3"></div>  </div></section> -<section class="dac-section dac-gray"><div class="wrap"> + +<section class="dac-section dac-gray" id="videos"><div class="wrap"> +  <h1 class="dac-section-title">Videos</h1> +  <div class="dac-section-subtitle"> +    New Android capabilities and the right way to use them in your apps. +  </div> + +  <div class="resource-widget resource-flow-layout col-16" +    data-query="collection:preview/landing/videos/first,type:youtube+tag:androidn" +    data-sortOrder="-timestamp" +    data-cardSizes="6x6" +    data-items-per-page="6" +    data-maxResults="15" +    data-initial-results="3"></div> +</div></section> + + +<section class="dac-section dac-light" id="resources"><div class="wrap">    <h1 class="dac-section-title">Resources</h1>    <div class="dac-section-subtitle">      Essential information to help you get your apps ready for Android N. diff --git a/docs/html/preview/setup-sdk.jd b/docs/html/preview/setup-sdk.jd index 3b479e25b675..58ca1d0b174a 100644 --- a/docs/html/preview/setup-sdk.jd +++ b/docs/html/preview/setup-sdk.jd @@ -76,6 +76,12 @@ Android N Preview SDK in Android Studio as follows:</p>  <h3 id="docs-dl">Get the N Preview reference documentation</h3> +<p class="note"> +  <strong>Note:</strong> The N Preview (API level 24) reference documentation +  is now available online at <a href= +  "{@docRoot}reference/">developer.android.com/reference/</a>. +</p> +  <p>    Detailed information about the Android N APIs is available in the N Preview    reference documentation, which you can download from the following table. @@ -95,7 +101,7 @@ Android N Preview SDK in Android Studio as follows:</p>        >n-preview-3-docs.zip</a></td>      <td width="100%">        MD5: 19bcfd057a1f9dd01ffbb3d8ff7b8d81<br> -      SHA-1: 9224bd4445cd7f653c4c294d362ccb195a2101e7  +      SHA-1: 9224bd4445cd7f653c4c294d362ccb195a2101e7      </td>    </tr>  </table> diff --git a/docs/html/topic/libraries/support-library/revisions.jd b/docs/html/topic/libraries/support-library/revisions.jd index 75257609cb30..88b0ca1fec0e 100644 --- a/docs/html/topic/libraries/support-library/revisions.jd +++ b/docs/html/topic/libraries/support-library/revisions.jd @@ -6,6 +6,124 @@ page.metaDescription=This page provides details about the Support Library packag  <p>This page provides details about the Support Library package releases.</p>  <div class="toggle-content opened"> +  <p id="rev24-0-0"> +    <a href="#" onclick="return toggleContent(this)"><img src= +    "{@docRoot}assets/images/styles/disclosure_up.png" class= +    "toggle-content-img" alt="">Android Support Library, revision 24.0.0</a> +    <em>(June 2016)</em> +  </p> + +  <div class="toggle-content-toggleme"> +    <dl> +      <dt> +        Changes for <a href= +        "{@docRoot}tools/support-library/features.html#v4">v4 Support +        Library</a>: +      </dt> + +      <dd> +          <ul> +            <li>Added <code>Fragment.commitNow()</code> +            for synchronous commit +            </li> + +            <li>Added <code>NotificationCompat.MessagingStyle</code> +            for multi-party conversations +            </li> + +            <li>Added <code>NotificationManagerCompat.areNotificationsEnabled()</code> +            and <code>getImportance()</code> +            </li> + +            <li>{@link android.support.v4.media.session.MediaSessionCompat} +            now mirrors the functionality of {@link +            android.media.session.MediaSession} and no longer calls {@link +            android.media.session.MediaSession#setMediaButtonReceiver +            setMediaButtonReceiver()} automatically +            </li> +          </ul> +          <p class="note"> +            <strong>Note:</strong> <a href= +            "{@docRoot}reference/android/support/v4/media/MediaBrowserServiceCompat.html" +            ><code>MediaBrowserServiceCompat</code></a> is compatible with +            devices running API Level 24 as of Support Library revision 24.0.0. +            Older versions of <a href= +            "{@docRoot}reference/android/support/v4/media/MediaBrowserServiceCompat.html" +            ><code>MediaBrowserServiceCompat</code></a> +            are <em>not</em> compatible with API Level 24. If your app uses +            <a href= +            "{@docRoot}reference/android/support/v4/media/MediaBrowserServiceCompat.html" +            ><code>MediaBrowserServiceCompat</code></a> and +            supports devices running API level 24, you must update to Support +            Library version 24.0.0 or later. +          </p> + +      <dt> +        Changes for <a href= +        "{@docRoot}tools/support-library/features.html#v7-appcompat">v7 +        appcompat library</a>: +      </dt> + +      <dd> +        <ul> +          <li>Added support for referencing themed {@link +            android.content.res.ColorStateList} objects from XML +          </li> +        </ul> +      </dd> + +      <dt> +        Changes for <a href= +        "{@docRoot}tools/support-library/features.html#design">Design Support +        Library</a>: +      </dt> + +      <dd> +        <ul> +          <li>Improvements to {@link android.support.design.widget.AppBarLayout} +            handling of elevation using {@link +            android.animation.StateListAnimator} +          </li> +        </ul> +      </dd> + +      <dt> +        Changes for <a href= +        "{@docRoot}topic/libraries/support-library/features.html#v17-leanback" +        >v17 Leanback library</a>: +      </dt> + +      <dd> +        <ul> +          <li>Added <code>OnboardingFragment</code> to provide first-run welcome +            and setup flow +          </li> +        </ul> +      </dd> + +      <dt> +        Changes for <a href= +        "{@docRoot}topic/libraries/support-library/features.html#custom-tabs" +        >custom tabs</a>: +      </dt> + +      <dd> +        <ul> +          <li>Added support for providing a {@link android.widget.RemoteViews} +            hierarchy for the secondary toolbar</li> +          <li>Added <code>CustomTabsClient.connectAndInitialize()</code> +            for one-line warm up</li> +        </ul> +      </dd> + +    </dl> +  </div> +</div> + +<!-- end of collapsible section: 24.0.0 --> + + +<div class="toggle-content closed">    <p id="rev23-4-0">      <a href="#" onclick="return toggleContent(this)"><img src=      "{@docRoot}assets/images/styles/disclosure_up.png" class= @@ -45,10 +163,9 @@ page.metaDescription=This page provides details about the Support Library packag        <dd>          <ul> -          <li>Added <!-- TODO: Link to method --> -             <code><a href= -            "{@docRoot}reference/android/support/v7/app/AppCompatDelegate.html"> -            AppCompatDelegate</a>.setCompatVectorFromResourcesEnabled()</code> +          <li>Added +            {@link android.support.v7.app.AppCompatDelegate#setCompatVectorFromResourcesEnabled +            AppCompatDelegate.setCompatVectorFromResourcesEnabled()}              method to re-enable usage of vector drawables in {@link              android.graphics.drawable.DrawableContainer} objects on devices              running Android 4.4 (API level 19) and lower. See <a href= @@ -118,8 +235,9 @@ page.metaDescription=This page provides details about the Support Library packag        <dd>          <ul> -          <li>Fixed a bug where <!-- TODO: Javadoc link --> -             <code>VectorDrawableCompat</code> does not render correctly in +          <li>Fixed a bug where {@link +            android.support.graphics.drawable.VectorDrawableCompat} does +            not render correctly in              {@link android.widget.TextView} on API level 23. (<a class=              "external-link" href=              "https://code.google.com/p/android/issues/detail?id=206227">Issue diff --git a/docs/html/training/basics/supporting-devices/languages.jd b/docs/html/training/basics/supporting-devices/languages.jd index 7d4d4743a50c..ba7c0160fe51 100644 --- a/docs/html/training/basics/supporting-devices/languages.jd +++ b/docs/html/training/basics/supporting-devices/languages.jd @@ -41,7 +41,7 @@ your string values.</p>  <p>To add support for more languages, create additional <code>values</code> directories inside  <code>res/</code> that include a hyphen and the ISO language code at the end of the  directory name. For example, <code>values-es/</code> is the directory containing simple -resourcess for the Locales with the language code "es".  Android loads the appropriate resources +resources for the Locales with the language code "es".  Android loads the appropriate resources  according to the locale settings of the device at run time. For more information, see   <a href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">Providing Alternative Resources</a>.</p> diff --git a/docs/html/training/material/compatibility.jd b/docs/html/training/material/compatibility.jd index 9ea88b2357b5..8ebe6f63cc39 100644 --- a/docs/html/training/material/compatibility.jd +++ b/docs/html/training/material/compatibility.jd @@ -127,7 +127,7 @@ the Android v7 Support Library with these limitations:</p>  <p>To use these features in versions of Android earlier than 5.0 (API level 21), include the  Android v7 Support Library in your project as a <a -href="{@docRoot}/studio/build/index.html#dependencies">Gradle dependency</a>:</p> +href="{@docRoot}studio/build/index.html#dependencies">Gradle dependency</a>:</p>  <pre>  dependencies { diff --git a/docs/html/training/wearables/data-layer/events.jd b/docs/html/training/wearables/data-layer/events.jd index 9bed9d548aad..20f219d69188 100644 --- a/docs/html/training/wearables/data-layer/events.jd +++ b/docs/html/training/wearables/data-layer/events.jd @@ -22,21 +22,21 @@ the call ends up making with listeners.  <h2 id="Wait">Wait for the Status of Data Layer Calls</h2>  <p>You'll notice that calls to the Data Layer API sometimes return a -<a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>, +<a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>,  such as -<a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.html#putDataItem(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.PutDataRequest)"><code>putDataItem()</code></a>. -As soon as the <a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a> is created, +<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.html#putDataItem(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.PutDataRequest)"><code>putDataItem()</code></a>. +As soon as the <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a> is created,  the operation is queued in the background. If you do nothing else after this, the operation  eventually completes silently. However, you'll usually want to do something with the result  after the operation completes, so the -<a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a> +<a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>  lets you wait for the result status, either synchronously or asynchronously.  </p>  <h3 id="async-waiting">Asynchronous calls</h3>  <p>If your code is running on the main UI thread, do not make blocking calls  to the Data Layer API. You can run the calls asynchronously by adding a callback method -to the <a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a> object, +to the <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a> object,  which fires when the operation is completed:</p>  <pre>  pendingResult.setResultCallback(new ResultCallback<DataItemResult>() { @@ -51,12 +51,12 @@ pendingResult.setResultCallback(new ResultCallback<DataItemResult>() {  <h3 id="sync-waiting">Synchronous calls</h3>  <p>If your code is running on a separate handler thread in a background service (which is the case -in a <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>), +in a <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>),  it's fine for the calls to block. In this case, you can call -<a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html#await()"><code>await()</code></a> -on the <a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a> +<a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html#await()"><code>await()</code></a> +on the <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>  object, which blocks until the request completes and returns a -<a href="{@docRoot}reference/com/google/android/gms/common/api/Result.html"><code>Result</code></a> +<a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/Result.html"><code>Result</code></a>  object:  </p> @@ -79,7 +79,7 @@ To listen for data layer events, you have two options:  </p>  <ul>     <li>Create a service that extends <a href -="https://developer.android.com/reference/com/google/android/gms/wearable/WearableListenerService.html"> +="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html">  {@code WearableListenerService}</a>.</li>     <li>Create an activity that implements <a  href="https://developer.android.com/reference/com/google/android/gms/wearable/DataApi.DataListener.html"> @@ -215,7 +215,7 @@ The next section explains how to use an intent filter with this listener.  <p>  An intent filter for the -<a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"> +<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html">  {@code WearableListenerService}</a> example shown in the previous section might look like this:  <pre> @@ -250,14 +250,14 @@ You can also match a literal path or path prefix. If you are matching by path  or path prefix, you must specify a wildcard or specific host.  If you do not do so, the system ignores the path you specified.  </p> +  <p>  For more information on the filter types that Wear supports, see the  API reference documentation for <a  href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService">  {@code WearableListenerService}</a>. - -  </p> +  <p>  For more information on data filters and matching rules, see the API reference  documentation for the <a @@ -265,7 +265,6 @@ href="{@docRoot}guide/topics/manifest/data-element.html">{@code data}</a>  manifest element.  </p> -  <p>When matching intent filters, there are two important rules to remember:</p>  <ul>      <li>If a scheme is not specified for the intent filter, the system ignores @@ -282,10 +281,10 @@ change. In such a case, you can listen for events in an activity by  implementing one or more of the following interfaces:  </p>  <ul> -  <li><a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code> +  <li><a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>    DataApi.DataListener</code></a></li> -  <li><a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.MessageListener.html"> +  <li><a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/MessageApi.MessageListener.html">    <code>MessageApi.MessageListener</code></a></li>    <li><a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/CapabilityApi.CapabilityListener.html">{@code CapabilityApi.CapabilityListener}</a></li> @@ -295,21 +294,21 @@ implementing one or more of the following interfaces:  <ol>  <li>Implement the desired interfaces.</li>  <li>In {@link android.app.Activity#onCreate onCreate()}, create an instance of -<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"><code>GoogleApiClient</code> +<a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApiClient.html"><code>GoogleApiClient</code>  </a>to work with the Data Layer API.</li>  <li> -In {@link android.app.Activity#onStart onStart()}, call <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()"> +In {@link android.app.Activity#onStart onStart()}, call <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()">  <code>connect()</code></a> to connect the client to Google Play services.  </li>  <li>When the connection to Google Play services is established, the system calls -<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)"><code>onConnected()</code></a>. This is where you call +<a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)"><code>onConnected()</code></a>. This is where you call -<a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.html#addListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.DataApi.DataListener)"> +<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.html#addListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.DataApi.DataListener)">  <code>DataApi.addListener()</code></a>, -<a href="{@docRoot}android/reference/com/google/android/gms/wearable/CapabilityApi.CapabilityListener"> +<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/MessageApi.html#addListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.MessageApi.MessageListener, android.net.Uri, int)">  <code>MessageApi.addListener()</code></a>, or  <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/CapabilityApi.html#addListener(com.google.android.gms.common.api.GoogleApiClient,%20com.google.android.gms.wearable.CapabilityApi.CapabilityListener,%20android.net.Uri,%20int)"> @@ -317,14 +316,16 @@ In {@link android.app.Activity#onStart onStart()}, call <a href="{@docRoot}refer  interested in listening for data layer events.</li>  <li>In {@link android.app.Activity#onStop onStop()}, unregister any listeners with -<a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.html#removeListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.DataApi.DataListener)"><code>DataApi.removeListener()</code></a>, -<a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.html#removeListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.MessageApi.MessageListener)"><code>MessageApi.removeListener()</code></a>, or -<a href="http://developer.android.com/reference/com/google/android/gms/wearable/MessageApi.html#removeListener(com.google.android.gms.common.api.GoogleApiClient,%20com.google.android.gms.wearable.MessageApi.MessageListener)"> +<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.html#removeListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.DataApi.DataListener)"><code>DataApi.removeListener()</code></a>, + +<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/MessageApi.html#removeListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.MessageApi.MessageListener)"><code>MessageApi.removeListener()</code></a>, or + +<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/CapabilityApi.html#removeListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.CapabilityApi.CapabilityListener)">  {@code CapabilityApi.removeListener()}</a>.</li>  <p>An alternative to adding listeners in -<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)"><code>onConnected()</code></a> +<a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)"><code>onConnected()</code></a>  and removing them in  {@link android.app.Activity#onStop onStop()} is to add a filtered listener in an activity’s {@link android.app.Activity#onResume onResume()} and  remove it in {@link android.app.Activity#onPause onPause()}, so as to only receive data that is relevant to the @@ -332,18 +333,18 @@ current application state.</p>  <li>Implement -<a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)"> +<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.DataListener.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)">  <code>onDataChanged()</code></a>,    <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/MessageApi.MessageListener.html#onMessageReceived(com.google.android.gms.wearable.MessageEvent)">    <code>onMessageReceived()</code></a>,    <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html#onCapabilityChanged(com.google.android.gms.wearable.CapabilityInfo)">  {@code onCapabilityChanged()}</a>, -or methods from <a href="http://developer.android.com/reference/com/google/android/gms/wearable/ChannelApi.ChannelListener.html"> +or methods from <a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/ChannelApi.ChannelListener.html">  Channel API listener methods</a>, depending on the interfaces that you implemented.</li>  </ol>  <p>Here's an example that implements -<a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>DataApi.DataListener</code></a>:</p> +<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>DataApi.DataListener</code></a>:</p>  <pre>  public class MainActivity extends Activity implements @@ -403,7 +404,7 @@ public class MainActivity extends Activity implements  <h3>Using Filters with Listener Activities</h3>  <p>  Just as you can specify intent filters for manifest-based -<a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"> +<a href="https://developers.google.com/android/reference/com/google/android/gms/wearable/WearableListenerService.html">  <code>WearableListenerService</code></a> objects, you can also use intent filters when registering a  listener through the Wearable API. The same rules are applicable to both  API-based listeners manifest-based listeners. @@ -411,7 +412,7 @@ API-based listeners manifest-based listeners.  <p>  A common pattern is to register a listener with a specific path or path prefix -in an activity’s{@link android.app.Activity#onResume onResume()} method, and to +in an activity’s {@link android.app.Activity#onResume onResume()} method, and to  remove the listener in the activity’s {@link android.app.Activity#onPause onPause()} method.  Implementing listeners in this fashion allows your application to more selectively receive events,  improving its design and efficiency. diff --git a/docs/html/training/wearables/watch-faces/performance.jd b/docs/html/training/wearables/watch-faces/performance.jd index 4a965456b173..a83a72b1f0f8 100644 --- a/docs/html/training/wearables/watch-faces/performance.jd +++ b/docs/html/training/wearables/watch-faces/performance.jd @@ -6,30 +6,117 @@ page.title=Optimizing Performance and Battery Life  <div id="tb">  <h2>This lesson teaches you to</h2>  <ol> +  <li><a href="#Basic">Basic Optimization</a></li> +  <li><a href="#Animations">Best Practices for Animations</a></li>    <li><a href="#ReduceSize">Reduce the Size of Your Bitmap Assets</a></li>    <li><a href="#CombineBitmaps">Combine Bitmap Assets</a></li>    <li><a href="#AntiAlias">Disable Anti-Aliasing when Drawing Scaled Bitmaps</a></li>    <li><a href="#OutDrawing">Move Expensive Operations Outside the Drawing Method</a></li> -  <li><a href="#SavePower">Follow Best Practices to Save Power</a></li>  </ol>  <h2>You should also read</h2>  <ul>    <li><a href="{@docRoot}design/wear/watchfaces.html">Watch Faces for Android Wear</a></li>    <li><a href="http://android-developers.blogspot.com/2014/12/making-performant-watch-face.html">  Making a performant watch face</a></li> +  <li><a href="http://android-developers.blogspot.com/2016/04/deprecation-of-bindlistener.html"> +Deprecation of BIND_LISTENER with Android Wear APIs</a></li>  </ul>  </div>  </div> -<p>In addition to accommodating notification cards and system indicators, you need to ensure that -the animations in your watch face run smoothly and that your service does not perform unnecessary -computations. Watch faces in Android Wear run continuously on the device, so it is critical -that your watch face uses power efficiently.</p> +<p>This lesson has tips for conserving power and improving performance. +A watch face runs continuously, so it must use power +efficiently. </p> -<p>This lesson provides some tips to speed up your animations and to measure and conserve -power on the device.</p> +<p>Services must not perform unnecessary computations. +Watch faces with animations must run smoothly while accommodating +notification cards and system indicators.</p> +<h2 id="Basic">Basic Optimization</h2> +<p>This section contains best practices for improving efficiency during +periods when a watch face is inactive.</p> + +<h3>Use callbacks in WatchFaceService.Engine</h3> + +<p>Ensure that your watch face performs +computations only when active; use callbacks +in <a href="{@docRoot}reference/android/support/wearable/watchface/ +WatchFaceService.Engine.html">{@code WatchFaceService.Engine}</a>. +Preferably, use the following methods of that class to determine if +the watch face is visible:</p> + +<ul> +  <li>{@code onVisibilityChanged(boolean)}</li> +  <li>{@code isVisible()}</li> +</ul> + +<p>Alternatively, use the following methods of the same class +(<a href="{@docRoot}reference/android/support/wearable/watchface/ +WatchFaceService.Engine.html">{@code WatchFaceService.Engine}</a>):</p> + +<ul> +  <li>{@code onCreate()}</li> +  <li>{@code onDestroy()}</li> +</ul> + +<h3>Use listeners registered with the DataApi interface</h3> + +<p>To listen for events, use live listeners that are registered +with <a href="https://developers.google.com/android/reference/com/google/ +android/gms/wearable/DataApi.html#addListener +(com.google.android.gms.common.api.GoogleApiClient, com. +google.android.gms.wearable.DataApi.DataListener)">{@code DataApi.addListener}</a>. +For an example, see <a href="{@docRoot}training/wearables/data-layer/ +data-items.html#ListenEvents">Syncing Data Items</a>.</p> + +<p>Do not use <a href="https://developers.google.com/ +android/reference/com/google/android/gms/wearable/ +WearableListenerService">{@code WearableListenerService}</a> to listen for +events, because it is +called whether or not a watch face is active. For more information, see +<a href="http://android-developers.blogspot.com/2016/04/ +deprecation-of-bindlistener.html">Deprecation of BIND_LISTENER +with Android Wear APIs</a>.</p> + +<p>Do not register a broadcast receiver in the Android manifest file +to get system events such as time zone changes, battery events, etc., because +the <a href="{@docRoot}reference/android/content/BroadcastReceiver.html">{@code BroadcastReceiver}</a> +is called whether or not a watch face is active. However, you can use the +<a href="{@docRoot}reference/android/content/Context.html#registerReceiver(android. +content.BroadcastReceiver, android.content.IntentFilter)">{@code registerReceiver}</a> method +of the {@code Context} class to register a receiver.</p> + +<h3>Monitor power consumption</h3> + +<p>The <a href="https://play.google.com/store/apps/details?id=com.google.android.wearable.app&hl=en"> +Android Wear companion app</a> enables developers and users to see how much battery +is consumed by different processes +on the wearable device (under <strong>Settings</strong> > <strong>Watch +battery</strong>).</p> + +<p>For information about features introduced in Android 5.0 that help you improve battery life, +see <a href="{@docRoot}about/versions/android-5.0.html#Power">Project Volta</a>.</p> + +<h2 id="Animations">Best Practices for Animations</h2> + +<p>The best practices in this section help to reduce the power consumption of animations.</p> + +<h3>Reduce the frame rate of animations</h3> + +<p>Animations are often computationally expensive and consume a significant amount of power. Most +animations look fluid at 30 frames per second, so you should avoid running your animations +at a higher frame rate.</p> + +<h3>Let the CPU sleep between animations</h3> + +<p>Animations and small changes to the contents of the watch face wake up the CPU. Your watch +face should let the CPU sleep in between animations. For example, you can use short bursts of +animation every second in interactive mode and then let the CPU sleep until the next second. +Letting the CPU sleep often, even briefly, can significantly reduce power consumption.</p> + +<p>To maximize battery life, use animations sparingly. Even a blinking colon wakes up the CPU with +every blink and hurts battery life.</p>  <h2 id="ReduceSize">Reduce the Size of Your Bitmap Assets</h2> @@ -68,16 +155,12 @@ by 97%.</p>  <p>Reducing the size of your bitmap assets as described in this section not only improves  the performance of your animations, but it also saves power.</p> - -  <h2 id="CombineBitmaps">Combine Bitmap Assets</h2>  <p>If you have bitmaps that are often drawn together, consider combining them into the same  graphic asset. You can often combine the background image in interactive mode with the tick  marks to avoid drawing two full-screen bitmaps every time the system redraws the watch face.</p> - -  <h2 id="AntiAlias">Disable Anti-Aliasing when Drawing Scaled Bitmaps</h2>  <p>When you draw a scaled bitmap on the {@link android.graphics.Canvas} object using the {@link @@ -139,35 +222,3 @@ implementation is short and  consistent across invocations. For more information, see  <a href="{@docRoot}tools/debugging/ddms.html">Using DDMS</a>.</p> - - -<h2 id="SavePower">Follow Best Practices to Save Power</h2> - -<p>In addition to the techniques described in the previous sections, follow the best -practices in this section to reduce the power consumption of your watch face.</p> - -<h3>Reduce the frame rate of animations</h3> - -<p>Animations are often computationally expensive and consume a significant amount of power. Most -animations look fluid at 30 frames per second, so you should avoid running your animations -at a higher frame rate.</p> - -<h3>Let the CPU sleep</h3> - -<p>Animations and small changes to the contents of the watch face wake up the CPU. Your watch -face should let the CPU sleep in between animations. For example, you can use short bursts of -animation every second in interactive mode and then let the CPU sleep until the next second. -Letting the CPU sleep often, even briefly, can significantly reduce power consumption.</p> - -<p>To maximize battery life, use animations sparingly. Even a blinking colon wakes up the CPU with -every blink and hurts battery life.</p> - -<h3>Monitor power consumption</h3> - -<p>The <a href="https://play.google.com/store/apps/details?id=com.google.android.wearable.app&hl=en"> -Android Wear companion app</a> lets developers and users see how much battery different processes -on the wearable device are consuming under <strong>Settings</strong> > <strong>Watch -battery</strong>.</p> - -<p>For more information about new features in Android 5.0 that help you improve battery life, -see <a href="{@docRoot}about/versions/android-5.0.html#Power">Project Volta</a>.</p> diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index b31c554c1914..5a3300ab8044 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -421,12 +421,14 @@ void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) {      prepareSubTree(info, childFunctorsNeedLayer, mDisplayList);      pushLayerUpdate(info); -    for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) { -        // If any vector drawable in the display list needs update, damage the node. -        if (vectorDrawable->isDirty()) { -            damageSelf(info); +    if (mDisplayList) { +        for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) { +            // If any vector drawable in the display list needs update, damage the node. +            if (vectorDrawable->isDirty()) { +                damageSelf(info); +            } +            vectorDrawable->setPropertyChangeWillBeConsumed(true);          } -        vectorDrawable->setPropertyChangeWillBeConsumed(true);      }      info.damageAccumulator->popTransform(); diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 597c5c56eec2..d4956bed25e8 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -149,6 +149,8 @@ void CanvasContext::setStopped(bool stopped) {              if (mEglManager.isCurrent(mEglSurface)) {                  mEglManager.makeCurrent(EGL_NO_SURFACE);              } +        } else if (mIsDirty && hasSurface()) { +            mRenderThread.postFrameCallback(this);          }      }  } @@ -231,6 +233,8 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,      freePrefetchedLayers(info.observer);      GL_CHECKPOINT(MODERATE); +    mIsDirty = true; +      if (CC_UNLIKELY(!mNativeSurface.get())) {          mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);          info.out.canDrawThisFrame = false; @@ -504,6 +508,7 @@ void CanvasContext::draw() {      // Even if we decided to cancel the frame, from the perspective of jank      // metrics the frame was swapped at this point      mCurrentFrameInfo->markSwapBuffers(); +    mIsDirty = false;      if (drew || mEglManager.damageRequiresSwap()) {          if (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty))) { diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index e739b2949cf9..a6eb7adc3568 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -187,7 +187,12 @@ private:      EglManager& mEglManager;      sp<Surface> mNativeSurface;      EGLSurface mEglSurface = EGL_NO_SURFACE; +    // stopped indicates the CanvasContext will reject actual redraw operations, +    // and defer repaint until it is un-stopped      bool mStopped = false; +    // CanvasContext is dirty if it has received an update that it has not +    // painted onto its surface. +    bool mIsDirty = false;      bool mBufferPreserved = false;      SwapBehavior mSwapBehavior = kSwap_default;      struct SwapHistory { diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp index b2997dfb357f..cf76a8691dcd 100644 --- a/libs/hwui/tests/unit/RenderNodeTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeTests.cpp @@ -16,13 +16,26 @@  #include <gtest/gtest.h> +#include "AnimationContext.h" +#include "DamageAccumulator.h" +#include "IContextFactory.h"  #include "RenderNode.h"  #include "TreeInfo.h" +#include "renderthread/CanvasContext.h"  #include "tests/common/TestUtils.h"  #include "utils/Color.h"  using namespace android;  using namespace android::uirenderer; +using namespace android::uirenderer::renderthread; + +class ContextFactory : public android::uirenderer::IContextFactory { +public: +    android::uirenderer::AnimationContext* createAnimationContext +        (android::uirenderer::renderthread::TimeLord& clock) override { +        return new android::uirenderer::AnimationContext(clock); +    } +};  TEST(RenderNode, hasParents) {      auto child = TestUtils::createNode(0, 0, 200, 400, @@ -89,3 +102,31 @@ TEST(RenderNode, releasedCallback) {      TestUtils::syncHierarchyPropertiesAndDisplayList(node);      EXPECT_EQ(0, refcnt);  } + +RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) { +    ContextFactory contextFactory; +    CanvasContext canvasContext(renderThread, false, nullptr, &contextFactory); +    TreeInfo info(TreeInfo::MODE_RT_ONLY, canvasContext); +    DamageAccumulator damageAccumulator; +    info.damageAccumulator = &damageAccumulator; +    info.observer = nullptr; + +    { +        auto nonNullDLNode = TestUtils::createNode(0, 0, 200, 400, +                [](RenderProperties& props, TestCanvas& canvas) { +            canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode); +        }); +        TestUtils::syncHierarchyPropertiesAndDisplayList(nonNullDLNode); +        EXPECT_TRUE(nonNullDLNode->getDisplayList()); +        nonNullDLNode->prepareTree(info); +    } + +    { +        auto nullDLNode = TestUtils::createNode(0, 0, 200, 400, nullptr); +        TestUtils::syncHierarchyPropertiesAndDisplayList(nullDLNode); +        EXPECT_FALSE(nullDLNode->getDisplayList()); +        nullDLNode->prepareTree(info); +    } + +    canvasContext.destroy(nullptr); +} diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index a4968235f5eb..c51048324c30 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -709,6 +709,8 @@ public class MediaPlayer extends PlayerBase       * played.       *       * @param sh the SurfaceHolder to use for video display +     * @throws IllegalStateException if the internal player engine has not been +     * initialized or has been released.       */      public void setDisplay(SurfaceHolder sh) {          mSurfaceHolder = sh; @@ -739,6 +741,8 @@ public class MediaPlayer extends PlayerBase       *       * @param surface The {@link Surface} to be used for the video portion of       * the media. +     * @throws IllegalStateException if the internal player engine has not been +     * initialized or has been released.       */      public void setSurface(Surface surface) {          if (mScreenOnWhilePlaying && surface != null) { diff --git a/packages/CtsShim/Android.mk b/packages/CtsShim/Android.mk index cd5b288617d8..fa6423ecb8c7 100644 --- a/packages/CtsShim/Android.mk +++ b/packages/CtsShim/Android.mk @@ -53,3 +53,4 @@ LOCAL_SRC_FILES := CtsShim.apk  include $(BUILD_PREBUILT) +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/packages/CtsShim/build/Android.mk b/packages/CtsShim/build/Android.mk index b550c1c253f3..bf6ae4151dae 100644 --- a/packages/CtsShim/build/Android.mk +++ b/packages/CtsShim/build/Android.mk @@ -17,7 +17,7 @@  LOCAL_PATH := $(my-dir)  ########################################################### -# Variant: Privileged app +# Variant: Privileged app upgrade  include $(CLEAR_VARS)  # this needs to be a privileged application @@ -28,15 +28,15 @@ LOCAL_SDK_VERSION := current  LOCAL_PROGUARD_ENABLED := disabled  LOCAL_DEX_PREOPT := false -LOCAL_PACKAGE_NAME := CtsShimPriv +LOCAL_PACKAGE_NAME := CtsShimPrivUpgrade -LOCAL_MANIFEST_FILE := shim_priv/AndroidManifest.xml +LOCAL_MANIFEST_FILE := shim_priv_upgrade/AndroidManifest.xml  include $(BUILD_PACKAGE) - +my_shim_priv_upgrade_apk := $(LOCAL_BUILT_MODULE)  ########################################################### -# Variant: Privileged app upgrade +# Variant: Privileged app  include $(CLEAR_VARS)  # this needs to be a privileged application @@ -47,12 +47,20 @@ LOCAL_SDK_VERSION := current  LOCAL_PROGUARD_ENABLED := disabled  LOCAL_DEX_PREOPT := false -LOCAL_PACKAGE_NAME := CtsShimPrivUpgrade +LOCAL_PACKAGE_NAME := CtsShimPriv -LOCAL_MANIFEST_FILE := shim_priv_upgrade/AndroidManifest.xml +# Generate the upgrade key by taking the hash of the built CtsShimPrivUpgrade apk +gen := $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),,true)/AndroidManifest.xml +$(gen): PRIVATE_CUSTOM_TOOL = sed -e "s/__HASH__/`sha512sum $(PRIVATE_INPUT_APK) | cut -d' ' -f1`/" $< >$@ +$(gen): PRIVATE_INPUT_APK := $(my_shim_priv_upgrade_apk) +$(gen): $(LOCAL_PATH)/shim_priv/AndroidManifest.xml $(my_shim_priv_upgrade_apk) +	$(transform-generated-source) -include $(BUILD_PACKAGE) +my_shim_priv_upgrade_apk := + +LOCAL_FULL_MANIFEST_FILE := $(gen) +include $(BUILD_PACKAGE)  ###########################################################  # Variant: System app diff --git a/packages/CtsShim/build/README b/packages/CtsShim/build/README index 1f154e1d70b0..333b87c8cb9d 100644 --- a/packages/CtsShim/build/README +++ b/packages/CtsShim/build/README @@ -6,31 +6,18 @@ must specify the singular APK that can be used to upgrade it.  NOTE: The need to include a binary on the system image may be deprecated if a  solution involving a temporarily writable /system partition is implemented. -MAKING THE PREBUILTS -In order to generate the upgrade key, the shim directory needs to be built multiple -times. First to generate the upgrade APK [so its hash can be obtained] and again -once the hash has been included as part of the pre-installed APK. -  build: -    $ mmm frameworks/base/packages/CtsShim/build - -update the manifest: -    $ sed -i -e "s/__HASH__/`sha512sum out/target/product/shamu/system/priv-app/CtsShimPrivUpgrade/CtsShimPrivUpgrade.apk | cut -d' ' -f1`/" \ -        frameworks/base/packages/CtsShim/build/shim_priv/AndroidManifest.xml +    $ tapas CtsShim CtsShimPriv CtsShimPrivUpgrade +    $ m -build: -    $ mmm frameworks/base/packages/CtsShim/build - -update prebuilts: -    $ cp out/target/product/shamu/system/priv-app/CtsShimPrivUpgrade/CtsShimPrivUpgrade.apk \ +local testing: +    $ cp $OUT/system/priv-app/CtsShimPrivUpgrade/CtsShimPrivUpgrade.apk \          cts/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp -    $ cp out/target/product/shamu/system/priv-app/CtsShimPriv/CtsShimPriv.apk \ +    $ cp $OUT/system/priv-app/CtsShimPriv/CtsShimPriv.apk \          frameworks/base/packages/CtsShim -    $ cp out/target/product/shamu/system/app/CtsShim/CtsShim.apk \ +    $ cp $OUT/system/app/CtsShim/CtsShim.apk \          frameworks/base/packages/CtsShim -revert manifest: -    $ pushd frameworks/base && git checkout -- packages/CtsShim/build/shim_priv/AndroidManifest.xml && popd - -Finally, upload and submit both the cts/ and frameworks/base/ repos. +For final submission, the APKs should be downloaded from the build server, then +submitted to the cts/ and frameworks/base/ repos. diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java index e071f6a07170..284827b57929 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java @@ -28,13 +28,10 @@ import android.support.v7.preference.PreferenceViewHolder;  import android.text.TextUtils;  import android.util.AttributeSet;  import android.util.SparseArray; -import android.view.View;  import android.widget.TextView;  import com.android.settingslib.R; -import java.util.Objects; -  public class AccessPointPreference extends Preference {      private static final int[] STATE_SECURED = { @@ -111,7 +108,6 @@ public class AccessPointPreference extends Preference {              mTitleView.setCompoundDrawablePadding(mBadgePadding);          }          view.itemView.setContentDescription(mContentDescription); -        view.itemView.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);      }      protected void updateIcon(int level, Context context) { @@ -152,7 +148,6 @@ public class AccessPointPreference extends Preference {       * Updates the title and summary; may indirectly call notifyChanged().       */      public void refresh() { -        boolean updated = false;          if (mForSavedNetworks) {              setTitle(mAccessPoint.getConfigName());          } else { @@ -164,28 +159,21 @@ public class AccessPointPreference extends Preference {          if (level != mLevel) {              mLevel = level;              updateIcon(mLevel, context); -            updated = true; +            notifyChanged();          }          updateBadge(context);          setSummary(mForSavedNetworks ? mAccessPoint.getSavedNetworkSummary()                  : mAccessPoint.getSettingsSummary()); -        CharSequence contentDescription = getTitle(); +        mContentDescription = getTitle();          if (getSummary() != null) { -            contentDescription = TextUtils.concat(contentDescription, ",", getSummary()); +            mContentDescription = TextUtils.concat(mContentDescription, ",", getSummary());          }          if (level >= 0 && level < WIFI_CONNECTION_STRENGTH.length) { -            contentDescription = TextUtils.concat(contentDescription, ",", +            mContentDescription = TextUtils.concat(mContentDescription, ",",                      getContext().getString(WIFI_CONNECTION_STRENGTH[level]));          } -        if (!Objects.equals(contentDescription, mContentDescription)) { -            mContentDescription = contentDescription; -            updated = true; -        } -        if (updated) { -            notifyChanged(); -        }      }      @Override diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java index 17f4dab93bf4..fd3bd92943c7 100644 --- a/packages/SystemUI/src/com/android/systemui/Interpolators.java +++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java @@ -36,6 +36,7 @@ public class Interpolators {      public static final Interpolator ACCELERATE = new AccelerateInterpolator();      public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();      public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f); +    public static final Interpolator CUSTOM_40_40 = new PathInterpolator(0.4f, 0f, 0.6f, 1f);      /**       * Interpolator to be used when animating a move based on a click. Pair with enough duration. diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java index 58782196df38..8bd38db05097 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java @@ -95,39 +95,34 @@ public class AssistDisclosure {      private class AssistDisclosureView extends View              implements ValueAnimator.AnimatorUpdateListener { -        public static final int TRACING_ANIMATION_DURATION = 600; -        public static final int ALPHA_IN_ANIMATION_DURATION = 450; -        public static final int ALPHA_OUT_ANIMATION_DURATION = 400; +        static final int FULL_ALPHA = 222; // 87% +        static final int ALPHA_IN_ANIMATION_DURATION = 400; +        static final int ALPHA_OUT_ANIMATION_DURATION = 300; +          private float mThickness;          private float mShadowThickness;          private final Paint mPaint = new Paint();          private final Paint mShadowPaint = new Paint(); -        private final ValueAnimator mTracingAnimator;          private final ValueAnimator mAlphaOutAnimator;          private final ValueAnimator mAlphaInAnimator;          private final AnimatorSet mAnimator; -        private float mTracingProgress = 0;          private int mAlpha = 0;          public AssistDisclosureView(Context context) {              super(context); -            mTracingAnimator = ValueAnimator.ofFloat(0, 1).setDuration(TRACING_ANIMATION_DURATION); -            mTracingAnimator.addUpdateListener(this); -            mTracingAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext, -                    R.interpolator.assist_disclosure_trace)); -            mAlphaInAnimator = ValueAnimator.ofInt(0, 255).setDuration(ALPHA_IN_ANIMATION_DURATION); +            mAlphaInAnimator = ValueAnimator.ofInt(0, FULL_ALPHA) +                    .setDuration(ALPHA_IN_ANIMATION_DURATION);              mAlphaInAnimator.addUpdateListener(this); -            mAlphaInAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); -            mAlphaOutAnimator = ValueAnimator.ofInt(255, 0).setDuration( +            mAlphaInAnimator.setInterpolator(Interpolators.CUSTOM_40_40); +            mAlphaOutAnimator = ValueAnimator.ofInt(FULL_ALPHA, 0).setDuration(                      ALPHA_OUT_ANIMATION_DURATION);              mAlphaOutAnimator.addUpdateListener(this); -            mAlphaOutAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); +            mAlphaOutAnimator.setInterpolator(Interpolators.CUSTOM_40_40);              mAnimator = new AnimatorSet(); -            mAnimator.play(mAlphaInAnimator).with(mTracingAnimator);              mAnimator.play(mAlphaInAnimator).before(mAlphaOutAnimator);              mAnimator.addListener(new AnimatorListenerAdapter() {                  boolean mCancelled; @@ -174,8 +169,6 @@ public class AssistDisclosure {              super.onDetachedFromWindow();              mAnimator.cancel(); - -            mTracingProgress = 0;              mAlpha = 0;          } @@ -197,45 +190,32 @@ public class AssistDisclosure {              final int width = getWidth();              final int height = getHeight();              float thickness = mThickness; -            final float pixelProgress = mTracingProgress * (width + height - 2 * thickness); - -            float bottomProgress = Math.min(pixelProgress, width / 2f); -            if (bottomProgress > 0) { -                drawBeam(canvas, -                        width / 2f - bottomProgress, -                        height - thickness, -                        width / 2f + bottomProgress, -                        height, paint, padding); -            } -            float sideProgress = Math.min(pixelProgress - bottomProgress, height - thickness); -            if (sideProgress > 0) { -                drawBeam(canvas, -                        0, -                        (height - thickness) - sideProgress, -                        thickness, -                        height - thickness, paint, padding); -                drawBeam(canvas, -                        width - thickness, -                        (height - thickness) - sideProgress, -                        width, -                        height - thickness, paint, padding); -            } - -            float topProgress = Math.min(pixelProgress - bottomProgress - sideProgress, -                    width / 2 - thickness); -            if (sideProgress > 0 && topProgress > 0) { -                drawBeam(canvas, -                        thickness, -                        0, -                        thickness + topProgress, -                        thickness, paint, padding); -                drawBeam(canvas, -                        (width - thickness) - topProgress, -                        0, -                        width - thickness, -                        thickness, paint, padding); -            } +            // bottom +            drawBeam(canvas, +                    0, +                    height - thickness, +                    width, +                    height, paint, padding); + +            // sides +            drawBeam(canvas, +                    0, +                    0, +                    thickness, +                    height - thickness, paint, padding); +            drawBeam(canvas, +                    width - thickness, +                    0, +                    width, +                    height - thickness, paint, padding); + +            // top +            drawBeam(canvas, +                    thickness, +                    0, +                    width - thickness, +                    thickness, paint, padding);          }          private void drawBeam(Canvas canvas, float left, float top, float right, float bottom, @@ -253,8 +233,6 @@ public class AssistDisclosure {                  mAlpha = (int) mAlphaOutAnimator.getAnimatedValue();              } else if (animation == mAlphaInAnimator) {                  mAlpha = (int) mAlphaInAnimator.getAnimatedValue(); -            } else if (animation == mTracingAnimator) { -                mTracingProgress = (float) mTracingAnimator.getAnimatedValue();              }              invalidate();          } diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java index 9fb8bd557b90..2c5c437bfab0 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java @@ -34,6 +34,7 @@ import android.util.IntProperty;  import android.util.Property;  import android.util.TypedValue;  import android.view.View; +import android.view.ViewGroup;  import android.view.ViewParent;  import android.view.ViewStub; @@ -285,6 +286,26 @@ public class Utilities {      }      /** +     * Returns whether this view, or one of its descendants have accessibility focus. +     */ +    public static boolean isDescendentAccessibilityFocused(View v) { +        if (v.isAccessibilityFocused()) { +            return true; +        } + +        if (v instanceof ViewGroup) { +            ViewGroup vg = (ViewGroup) v; +            int childCount = vg.getChildCount(); +            for (int i = 0; i < childCount; i++) { +                if (isDescendentAccessibilityFocused(vg.getChildAt(i))) { +                    return true; +                } +            } +        } +        return false; +    } + +    /**       * Returns the application configuration, which is independent of the activity's current       * configuration in multiwindow.       */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java index 41869dd5f417..e503c56437a4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java @@ -209,7 +209,9 @@ public class RecentsTransitionHelper {              EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront));          } else {              // Dismiss the task if we fail to launch it -            taskView.dismissTask(); +            if (taskView != null) { +                taskView.dismissTask(); +            }              // Keep track of failed launches              EventBus.getDefault().send(new LaunchTaskFailedEvent()); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 231360e46ae9..586a8bcd8920 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -44,6 +44,7 @@ import android.view.ViewGroup;  import android.view.accessibility.AccessibilityEvent;  import android.view.accessibility.AccessibilityNodeInfo;  import android.widget.FrameLayout; +import android.widget.ScrollView;  import com.android.internal.logging.MetricsLogger;  import com.android.internal.logging.MetricsProto.MetricsEvent; @@ -170,7 +171,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal      @ViewDebug.ExportedProperty(category="recents")      private boolean mEnterAnimationComplete = false;      @ViewDebug.ExportedProperty(category="recents") -    private boolean mTouchExplorationEnabled; +    boolean mTouchExplorationEnabled;      @ViewDebug.ExportedProperty(category="recents")      boolean mScreenPinningEnabled; @@ -579,7 +580,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal              if (task.isFreeformTask() || (transform != null && transform.visible)) {                  mTmpTaskViewMap.put(task.key, tv);              } else { -                if (mTouchExplorationEnabled) { +                if (mTouchExplorationEnabled && Utilities.isDescendentAccessibilityFocused(tv)) {                      lastFocusedTaskIndex = taskIndex;                      resetFocusedTask(task);                  } @@ -630,12 +631,14 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal          // Update the focus if the previous focused task was returned to the view pool          if (lastFocusedTaskIndex != -1) { -            if (lastFocusedTaskIndex < visibleTaskRange[1]) { -                setFocusedTask(visibleTaskRange[1], false /* scrollToTask */, -                        true /* requestViewFocus */); -            } else { -                setFocusedTask(visibleTaskRange[0], false /* scrollToTask */, -                        true /* requestViewFocus */); +            int newFocusedTaskIndex = (lastFocusedTaskIndex < visibleTaskRange[1]) +                    ? visibleTaskRange[1] +                    : visibleTaskRange[0]; +            setFocusedTask(newFocusedTaskIndex, false /* scrollToTask */, +                    true /* requestViewFocus */); +            TaskView focusedTaskView = getChildViewForTask(mFocusedTask); +            if (focusedTaskView != null) { +                focusedTaskView.requestAccessibilityFocus();              }          }      } @@ -938,24 +941,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal       *                            focus.       */      public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated) { -        setRelativeFocusedTask(forward, stackTasksOnly, animated, false); -    } - -    /** -     * Sets the focused task relative to the currently focused task. -     * -     * @param forward whether to go to the next task in the stack (along the curve) or the previous -     * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and -     *                       if the currently focused task is not a stack task, will set the focus -     *                       to the first visible stack task -     * @param animated determines whether to actually draw the highlight along with the change in -     *                            focus. -     * @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll -     *                               happens. -     */ -    public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated, -                                       boolean cancelWindowAnimations) { -        setRelativeFocusedTask(forward, stackTasksOnly, animated, cancelWindowAnimations, 0); +        setRelativeFocusedTask(forward, stackTasksOnly, animated, false, 0);      }      /** @@ -972,13 +958,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal       * @param timerIndicatorDuration the duration to initialize the auto-advance timer indicator       */      public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated, -                                       boolean cancelWindowAnimations, -                                       int timerIndicatorDuration) { -        int newIndex = mStack.indexOfStackTask(mFocusedTask); -        if (mFocusedTask != null) { +                                       boolean cancelWindowAnimations, int timerIndicatorDuration) { +        Task focusedTask = getFocusedTask(); +        int newIndex = mStack.indexOfStackTask(focusedTask); +        if (focusedTask != null) {              if (stackTasksOnly) {                  List<Task> tasks =  mStack.getStackTasks(); -                if (mFocusedTask.isFreeformTask()) { +                if (focusedTask.isFreeformTask()) {                      // Try and focus the front most stack task                      TaskView tv = getFrontMostTaskView(stackTasksOnly);                      if (tv != null) { @@ -1054,6 +1040,25 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal          return mFocusedTask;      } +    /** +     * Returns the accessibility focused task. +     */ +    Task getAccessibilityFocusedTask() { +        List<TaskView> taskViews = getTaskViews(); +        int taskViewCount = taskViews.size(); +        for (int i = 0; i < taskViewCount; i++) { +            TaskView tv = taskViews.get(i); +            if (Utilities.isDescendentAccessibilityFocused(tv)) { +                return tv.getTask(); +            } +        } +        TaskView frontTv = getFrontMostTaskView(true /* stackTasksOnly */); +        if (frontTv != null) { +            return frontTv.getTask(); +        } +        return null; +    } +      @Override      public void onInitializeAccessibilityEvent(AccessibilityEvent event) {          super.onInitializeAccessibilityEvent(event); @@ -1078,21 +1083,23 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal          super.onInitializeAccessibilityNodeInfo(info);          List<TaskView> taskViews = getTaskViews();          int taskViewCount = taskViews.size(); -        if (taskViewCount > 1 && mFocusedTask != null) { +        if (taskViewCount > 1) { +            // Find the accessibility focused task +            Task focusedTask = getAccessibilityFocusedTask();              info.setScrollable(true); -            int focusedTaskIndex = mStack.indexOfStackTask(mFocusedTask); +            int focusedTaskIndex = mStack.indexOfStackTask(focusedTask);              if (focusedTaskIndex > 0) { -                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); -            } -            if (focusedTaskIndex < mStack.getTaskCount() - 1) {                  info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);              } +            if (0 <= focusedTaskIndex && focusedTaskIndex < mStack.getTaskCount() - 1) { +                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); +            }          }      }      @Override      public CharSequence getAccessibilityClassName() { -        return TaskStackView.class.getName(); +        return ScrollView.class.getName();      }      @Override @@ -1100,14 +1107,20 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal          if (super.performAccessibilityAction(action, arguments)) {              return true;          } -        switch (action) { -            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: { -                setRelativeFocusedTask(true, false /* stackTasksOnly */, false /* animated */); -                return true; -            } -            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: { -                setRelativeFocusedTask(false, false /* stackTasksOnly */, false /* animated */); -                return true; +        Task focusedTask = getAccessibilityFocusedTask(); +        int taskIndex = mStack.indexOfStackTask(focusedTask); +        if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) { +            switch (action) { +                case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: { +                    setFocusedTask(taskIndex + 1, true /* scrollToTask */, true /* requestViewFocus */, +                            0); +                    return true; +                } +                case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: { +                    setFocusedTask(taskIndex - 1, true /* scrollToTask */, true /* requestViewFocus */, +                            0); +                    return true; +                }              }          }          return false; @@ -1489,6 +1502,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal          unbindTaskView(tv, task);          // Reset the view properties and view state +        tv.clearAccessibilityFocus();          tv.resetViewProperties();          tv.setFocusedState(false, false /* requestViewFocus */);          tv.setClipViewInStack(false); @@ -1949,6 +1963,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal                          RecentsActivityLaunchState launchState = config.getLaunchState();                          setFocusedTask(mStack.indexOfStackTask(mFocusedTask),                                  false /* scrollToTask */, launchState.launchedWithAltTab); +                        TaskView focusedTaskView = getChildViewForTask(mFocusedTask); +                        if (mTouchExplorationEnabled && focusedTaskView != null) { +                            focusedTaskView.requestAccessibilityFocus(); +                        }                      }                      EventBus.getDefault().send(new EnterRecentsTaskStackAnimationCompletedEvent()); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java index b554a467c7ce..67a2595c90cd 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java @@ -343,7 +343,9 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {                      }                      // Reset the focused task after the user has scrolled -                    mSv.resetFocusedTask(mSv.getFocusedTask()); +                    if (!mSv.mTouchExplorationEnabled) { +                        mSv.resetFocusedTask(mSv.getFocusedTask()); +                    }                  } else if (mActiveTaskView == null) {                      // This tap didn't start on a task.                      maybeHideRecentsFromBackgroundTap((int) ev.getX(), (int) ev.getY()); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index 612c41d07a04..4ecdd7788001 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -133,8 +133,6 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks      @ViewDebug.ExportedProperty(deepExport=true, prefix="task_")      private Task mTask;      @ViewDebug.ExportedProperty(category="recents") -    private boolean mTaskDataLoaded; -    @ViewDebug.ExportedProperty(category="recents")      private boolean mClipViewInStack = true;      @ViewDebug.ExportedProperty(category="recents")      private boolean mTouchExplorationEnabled; @@ -451,16 +449,12 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks       * Explicitly sets the focused state of this task.       */      public void setFocusedState(boolean isFocused, boolean requestViewFocus) { -        SystemServicesProxy ssp = Recents.getSystemServices();          if (isFocused) {              if (requestViewFocus && !isFocused()) {                  requestFocus();              } -            if (requestViewFocus && !isAccessibilityFocused() && ssp.isTouchExplorationEnabled()) { -                requestAccessibilityFocus(); -            }          } else { -            if (isAccessibilityFocused() && ssp.isTouchExplorationEnabled()) { +            if (isAccessibilityFocused() && mTouchExplorationEnabled) {                  clearAccessibilityFocus();              }          } @@ -622,7 +616,6 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks          // Update each of the views to the new task data          mThumbnailView.onTaskDataLoaded(thumbnailInfo);          mHeaderView.onTaskDataLoaded(); -        mTaskDataLoaded = true;      }      @Override @@ -631,7 +624,6 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks          mTask.removeCallback(this);          mThumbnailView.unbindFromTask();          mHeaderView.unbindFromTask(mTouchExplorationEnabled); -        mTaskDataLoaded = false;      }      @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index aedc7df0ebc3..28a6851307fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -193,6 +193,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {          mGroupExpansionChanging = changing;      } +    @Override +    public void setActualHeightAnimating(boolean animating) { +        if (mPrivateLayout != null) { +            mPrivateLayout.setContentHeightAnimating(animating); +        } +    } +      public NotificationContentView getPrivateLayout() {          return mPrivateLayout;      } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index 2c302ed1af4e..b4f90c60b84e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -432,6 +432,8 @@ public abstract class ExpandableView extends FrameLayout {          return false;      } +    public void setActualHeightAnimating(boolean animating) {} +      /**       * A listener notifying when {@link #getActualHeight} changes.       */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index 21fed3c47689..a11263a3b9f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -106,13 +106,19 @@ public class NotificationContentView extends FrameLayout {      private boolean mExpandable;      private boolean mClipToActualHeight = true;      private ExpandableNotificationRow mContainingNotification; +    /** The visible type at the start of a touch driven transformation */      private int mTransformationStartVisibleType; +    /** The visible type at the start of an animation driven transformation */ +    private int mAnimationStartVisibleType = UNDEFINED;      private boolean mUserExpanding;      private int mSingleLineWidthIndention;      private boolean mForceSelectNextLayout = true;      private PendingIntent mPreviousExpandedRemoteInputIntent;      private PendingIntent mPreviousHeadsUpRemoteInputIntent; +    private int mContentHeightAtAnimationStart = UNDEFINED; + +      public NotificationContentView(Context context, AttributeSet attrs) {          super(context, attrs);          mHybridGroupManager = new HybridGroupManager(getContext(), this); @@ -258,7 +264,14 @@ public class NotificationContentView extends FrameLayout {      @Override      protected void onLayout(boolean changed, int left, int top, int right, int bottom) { +        int previousHeight = 0; +        if (mExpandedChild != null) { +            previousHeight = mExpandedChild.getHeight(); +        }          super.onLayout(changed, left, top, right, bottom); +        if (previousHeight != 0 && mExpandedChild.getHeight() != previousHeight) { +            mContentHeightAtAnimationStart = previousHeight; +        }          updateClipping();          invalidateOutline();          selectLayout(false /* animate */, mForceSelectNextLayout /* force */); @@ -408,24 +421,54 @@ public class NotificationContentView extends FrameLayout {       *         height, the notification is clipped instead of being further shrunk.       */      private int getMinContentHeightHint() { -        if (mIsChildInGroup && (mVisibleType == VISIBLE_TYPE_SINGLELINE -                || mTransformationStartVisibleType == VISIBLE_TYPE_SINGLELINE)) { +        if (mIsChildInGroup && isVisibleOrTransitioning(VISIBLE_TYPE_SINGLELINE)) {              return mContext.getResources().getDimensionPixelSize(                          com.android.internal.R.dimen.notification_action_list_height);          } + +        // Transition between heads-up & expanded, or pinned. +        if (mHeadsUpChild != null && mExpandedChild != null) { +            boolean transitioningBetweenHunAndExpanded = +                    isTransitioningFromTo(VISIBLE_TYPE_HEADSUP, VISIBLE_TYPE_EXPANDED) || +                    isTransitioningFromTo(VISIBLE_TYPE_EXPANDED, VISIBLE_TYPE_HEADSUP); +            boolean pinned = !isVisibleOrTransitioning(VISIBLE_TYPE_CONTRACTED) && mIsHeadsUp; +            if (transitioningBetweenHunAndExpanded || pinned) { +                return Math.min(mHeadsUpChild.getHeight(), mExpandedChild.getHeight()); +            } +        } + +        // Size change of the expanded version +        if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0 +                && mExpandedChild != null) { +            return Math.min(mContentHeightAtAnimationStart, mExpandedChild.getHeight()); +        } +          int hint; -        if (mHeadsUpChild != null) { +        if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {              hint = mHeadsUpChild.getHeight(); +        } else if (mExpandedChild != null) { +            hint = mExpandedChild.getHeight();          } else {              hint = mContractedChild.getHeight() + mContext.getResources().getDimensionPixelSize(                      com.android.internal.R.dimen.notification_action_list_height);          } -        if (mExpandedChild != null) { + +        if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {              hint = Math.min(hint, mExpandedChild.getHeight());          }          return hint;      } +    private boolean isTransitioningFromTo(int from, int to) { +        return (mTransformationStartVisibleType == from || mAnimationStartVisibleType == from) +                && mVisibleType == to; +    } + +    private boolean isVisibleOrTransitioning(int type) { +        return mVisibleType == type || mTransformationStartVisibleType == type +                || mAnimationStartVisibleType == type; +    } +      private void updateContentTransformation() {          int visibleType = calculateVisibleType();          if (visibleType != mVisibleType) { @@ -656,6 +699,7 @@ public class NotificationContentView extends FrameLayout {              shownView.setVisible(true);              return;          } +        mAnimationStartVisibleType = mVisibleType;          shownView.transformFrom(hiddenView);          getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);          hiddenView.transformTo(shownView, new Runnable() { @@ -664,6 +708,7 @@ public class NotificationContentView extends FrameLayout {                  if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {                      hiddenView.setVisible(false);                  } +                mAnimationStartVisibleType = UNDEFINED;              }          });      } @@ -1082,4 +1127,10 @@ public class NotificationContentView extends FrameLayout {              mHeadsUpRemoteInput.setRemoved();          }      } + +    public void setContentHeightAnimating(boolean animating) { +        if (!animating) { +            mContentHeightAtAnimationStart = UNDEFINED; +        } +    }  } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActionListTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActionListTransformState.java index c0373bef0f5a..8c725446a3cb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActionListTransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActionListTransformState.java @@ -44,6 +44,16 @@ public class ActionListTransformState extends TransformState {      }      @Override +    public void transformViewFullyFrom(TransformState otherState, float transformationAmount) { +        // Don't do Y transform - let the wrapper handle this based on the content height +    } + +    @Override +    public void transformViewFullyTo(TransformState otherState, float transformationAmount) { +        // Don't do Y transform - let the wrapper handle this based on the content height +    } + +    @Override      protected void resetTransformedView() {          // We need to keep the Y transformation, because this is used to keep the action list          // aligned at the bottom, unrelated to transforms. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMessagingTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMessagingTemplateViewWrapper.java new file mode 100644 index 000000000000..ff2febfa38f8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMessagingTemplateViewWrapper.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar.notification; + +import com.android.internal.widget.MessagingLinearLayout; +import com.android.systemui.statusbar.ExpandableNotificationRow; +import com.android.systemui.statusbar.TransformableView; + +import android.content.Context; +import android.service.notification.StatusBarNotification; +import android.view.View; + +/** + * Wraps a notification containing a messaging template + */ +public class NotificationMessagingTemplateViewWrapper extends NotificationTemplateViewWrapper { + +    private View mContractedMessage; + +    protected NotificationMessagingTemplateViewWrapper(Context ctx, View view, +            ExpandableNotificationRow row) { +        super(ctx, view, row); +    } + +    private void resolveViews() { +        mContractedMessage = null; + +        View container = mView.findViewById(com.android.internal.R.id.notification_messaging); +        if (container instanceof MessagingLinearLayout +                && ((MessagingLinearLayout) container).getChildCount() > 0) { +            MessagingLinearLayout messagingContainer = (MessagingLinearLayout) container; + +            // Only consider the first child - transforming to a position other than the first +            // looks bad because we have to move across other messages that are fading in. +            View child = messagingContainer.getChildAt(0); +            if (child.getId() == messagingContainer.getContractedChildId()) { +                mContractedMessage = child; +            } +        } +    } + +    @Override +    public void notifyContentUpdated(StatusBarNotification notification) { +        // Reinspect the notification. Before the super call, because the super call also updates +        // the transformation types and we need to have our values set by then. +        resolveViews(); +        super.notifyContentUpdated(notification); +    } + +    @Override +    protected void updateTransformedTypes() { +        // This also clears the existing types +        super.updateTransformedTypes(); +        if (mContractedMessage != null) { +            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT, +                    mContractedMessage); +        } +    } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java index 22519e6e4de3..16348dfe5818 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java @@ -50,6 +50,8 @@ public abstract class NotificationViewWrapper implements TransformableView {                  return new NotificationBigTextTemplateViewWrapper(ctx, v, row);              } else if ("media".equals(v.getTag()) || "bigMediaNarrow".equals(v.getTag())) {                  return new NotificationMediaTemplateViewWrapper(ctx, v, row); +            } else if ("messaging".equals(v.getTag())) { +                return new NotificationMessagingTemplateViewWrapper(ctx, v, row);              }              return new NotificationTemplateViewWrapper(ctx, v, row);          } else if (v instanceof NotificationHeaderView) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java index 6d0fbb15ca64..58fbd4c954d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java @@ -80,14 +80,16 @@ public class AutoTileManager {              new NightModeController.Listener() {          @Override          public void onNightModeChanged() { -            mHost.addTile("night"); -            Prefs.putBoolean(mContext, Key.QS_NIGHT_ADDED, true); -            mHandler.post(new Runnable() { -                @Override -                public void run() { -                    mHost.getNightModeController().removeListener(mNightModeListener); -                } -            }); +            if (mHost.getNightModeController().isEnabled()) { +                mHost.addTile("night"); +                Prefs.putBoolean(mContext, Key.QS_NIGHT_ADDED, true); +                mHandler.post(new Runnable() { +                    @Override +                    public void run() { +                        mHost.getNightModeController().removeListener(mNightModeListener); +                    } +                }); +            }          }          @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java index 186005cc2162..a92422a66a22 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java @@ -21,7 +21,8 @@ import android.content.Context;  import android.content.Intent;  import android.content.IntentFilter;  import android.content.res.TypedArray; -import android.text.format.DateFormat; +import android.icu.text.DateFormat; +import android.icu.text.DisplayContext;  import android.util.AttributeSet;  import android.widget.TextView; @@ -36,7 +37,7 @@ public class DateView extends TextView {      private final Date mCurrentTime = new Date(); -    private SimpleDateFormat mDateFormat; +    private DateFormat mDateFormat;      private String mLastText;      private String mDatePattern; @@ -100,8 +101,9 @@ public class DateView extends TextView {      protected void updateClock() {          if (mDateFormat == null) {              final Locale l = Locale.getDefault(); -            final String fmt = DateFormat.getBestDateTimePattern(l, mDatePattern); -            mDateFormat = new SimpleDateFormat(fmt, l); +            DateFormat format = DateFormat.getInstanceForSkeleton(mDatePattern, l); +            format.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE); +            mDateFormat = format;          }          mCurrentTime.setTime(System.currentTimeMillis()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java index 0f94227c6184..7ac0d803e24e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -485,6 +485,7 @@ public class StackStateAnimator {                  child.setTag(TAG_ANIMATOR_HEIGHT, null);                  child.setTag(TAG_START_HEIGHT, null);                  child.setTag(TAG_END_HEIGHT, null); +                child.setActualHeightAnimating(false);                  if (!mWasCancelled && child instanceof ExpandableNotificationRow) {                      ((ExpandableNotificationRow) child).setGroupExpansionChanging(                              false /* isExpansionChanging */); @@ -505,6 +506,7 @@ public class StackStateAnimator {          child.setTag(TAG_ANIMATOR_HEIGHT, animator);          child.setTag(TAG_START_HEIGHT, child.getActualHeight());          child.setTag(TAG_END_HEIGHT, newEndValue); +        child.setActualHeightAnimating(true);      }      private void startInsetAnimation(final ExpandableView child, diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java index c6992aad068e..1973e058e3e9 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java @@ -977,6 +977,7 @@ public class VolumeDialog implements TunerService.Tunable {              final int density = newConfig.densityDpi;              if (density != mDensity) {                  mDialog.dismiss(); +                mZenFooter.cleanup();                  initDialog();              }              updateWindowWidthH(); diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java index 65975d9e9f33..f01e95fa3873 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java +++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java @@ -79,18 +79,11 @@ public class ZenFooter extends LinearLayout {          mZen = controller.getZen();          mConfig = controller.getConfig();          mController = controller; -        update(); -    } - -    @Override -    protected void onAttachedToWindow() { -        super.onAttachedToWindow();          mController.addCallback(mZenCallback); +        update();      } -    @Override -    protected void onDetachedFromWindow() { -        super.onDetachedFromWindow(); +    public void cleanup() {          mController.removeCallback(mZenCallback);      } diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 8ad6e6afcce5..400c4a700a8e 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -3073,9 +3073,14 @@ class AlarmManagerService extends SystemService {                  fs.numWakeup++;                  if (alarm.workSource != null && alarm.workSource.size() > 0) {                      for (int wi=0; wi<alarm.workSource.size(); wi++) { +                        final String wsName = alarm.workSource.getName(wi); +                        if (wsName == null) { +                            Slog.w(TAG, "Null worksource name for alarm " + alarm); +                        }                          ActivityManagerNative.noteWakeupAlarm(                                  alarm.operation, alarm.workSource.get(wi), -                                alarm.workSource.getName(wi), alarm.statsTag); +                                (wsName != null) ? wsName : alarm.packageName, +                                alarm.statsTag);                      }                  } else {                      ActivityManagerNative.noteWakeupAlarm( diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 1adefc4cbddf..be440ed2bfe1 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -3566,14 +3566,14 @@ public class ConnectivityService extends IConnectivityManager.Stub                      .build();              try { -                notificationManager.notify(NOTIFICATION_ID, id, notification); +                notificationManager.notifyAsUser(NOTIFICATION_ID, id, notification, UserHandle.ALL);              } catch (NullPointerException npe) {                  loge("setNotificationVisible: visible notificationManager npe=" + npe);                  npe.printStackTrace();              }          } else {              try { -                notificationManager.cancel(NOTIFICATION_ID, id); +                notificationManager.cancelAsUser(NOTIFICATION_ID, id, UserHandle.ALL);              } catch (NullPointerException npe) {                  loge("setNotificationVisible: cancel notificationManager npe=" + npe);                  npe.printStackTrace(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 10ad482ce2a6..63f122b4523c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -5375,17 +5375,18 @@ public final class ActivityManagerService extends ActivityManagerNative          userId = mUserController.handleIncomingUser(pid, uid, userId, false,                  ALLOW_FULL_ONLY, "clearApplicationUserData", null); -        final DevicePolicyManagerInternal dpmi = LocalServices -                .getService(DevicePolicyManagerInternal.class); -        if (dpmi != null && dpmi.hasDeviceOwnerOrProfileOwner(packageName, userId)) { -            throw new SecurityException("Cannot clear data for a device owner or a profile owner"); -        }          long callingId = Binder.clearCallingIdentity();          try {              IPackageManager pm = AppGlobals.getPackageManager();              int pkgUid = -1;              synchronized(this) { +                if (getPackageManagerInternalLocked().canPackageBeWiped( +                        userId, packageName)) { +                    throw new SecurityException( +                            "Cannot clear data for a device owner or a profile owner"); +                } +                  try {                      pkgUid = pm.getPackageUid(packageName, MATCH_UNINSTALLED_PACKAGES, userId);                  } catch (RemoteException e) { @@ -17783,6 +17784,11 @@ public final class ActivityManagerService extends ActivityManagerNative                                  getPackageManagerInternalLocked().getApplicationInfo(                                          ssp,                                          userId); +                        if (aInfo == null) { +                            Slog.w(TAG, "Dropping ACTION_PACKAGE_REPLACED for non-existent pkg:" +                                    + " ssp=" + ssp + " data=" + data); +                            return ActivityManager.BROADCAST_SUCCESS; +                        }                          mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);                          sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REPLACED,                                  new String[] {ssp}, userId); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index d83a750ccbb3..df85cfa939e3 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -2581,11 +2581,14 @@ final class ActivityStack {      }      private void insertTaskAtTop(TaskRecord task, ActivityRecord newActivity) { +        boolean isLastTaskOverHome = false;          // If the moving task is over home stack, transfer its return type to next task          if (task.isOverHomeStack()) {              final TaskRecord nextTask = getNextTask(task);              if (nextTask != null) {                  nextTask.setTaskToReturnTo(task.getTaskToReturnTo()); +            } else { +                isLastTaskOverHome = true;              }          } @@ -2595,7 +2598,10 @@ final class ActivityStack {              ActivityStack lastStack = mStackSupervisor.getLastStack();              final boolean fromHome = lastStack.isHomeStack();              if (!isHomeStack() && (fromHome || topTask() != task)) { -                int returnToType = APPLICATION_ACTIVITY_TYPE; +                // If it's a last task over home - we default to keep its return to type not to +                // make underlying task focused when this one will be finished. +                int returnToType = isLastTaskOverHome +                        ? task.getTaskToReturnTo() : APPLICATION_ACTIVITY_TYPE;                  if (fromHome && StackId.allowTopTaskToReturnHome(mStackId)) {                      returnToType = lastStack.topTask() == null                              ? HOME_ACTIVITY_TYPE : lastStack.topTask().taskType; diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java index 566d8d95d952..a2c2040b5c51 100644 --- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java @@ -147,6 +147,9 @@ class ActivityStartInterceptor {          }          DevicePolicyManagerInternal devicePolicyManager = LocalServices.getService(                  DevicePolicyManagerInternal.class); +        if (devicePolicyManager == null) { +            return false; +        }          mIntent = devicePolicyManager.createPackageSuspendedDialogIntent(                  mAInfo.packageName, mUserId);          mCallingPid = mRealCallingPid; diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index 1b2ccd7e43ad..9220aa388f3e 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -22,6 +22,7 @@ import android.annotation.Nullable;  import android.app.ActivityManager;  import android.app.ActivityManagerNative;  import android.app.AppOpsManager; +import android.app.job.JobInfo;  import android.content.BroadcastReceiver;  import android.content.ComponentName;  import android.content.ContentProvider; @@ -512,6 +513,16 @@ public final class ContentService extends IContentService.Stub {          syncAsUser(request, UserHandle.getCallingUserId());      } +    private long clampPeriod(long period) { +        long minPeriod = JobInfo.getMinPeriodMillis() / 1000; +        if (period < minPeriod) { +            Slog.w(TAG, "Requested poll frequency of " + period +                    + " seconds being rounded up to " + minPeriod + "s."); +            period = minPeriod; +        } +        return period; +    } +      /**       * If the user id supplied is different to the calling user, the caller must hold the       * INTERACT_ACROSS_USERS_FULL permission. @@ -539,11 +550,8 @@ public final class ContentService extends IContentService.Stub {                  SyncStorageEngine.EndPoint info;                  info = new SyncStorageEngine.EndPoint(                          request.getAccount(), request.getProvider(), userId); -                if (runAtTime < 3600) { -                    Slog.w(TAG, "Requested poll frequency of " + runAtTime -                            + " seconds being rounded up to 1 hour."); -                    runAtTime = 3600; -                } + +                runAtTime = clampPeriod(runAtTime);                  // Schedule periodic sync.                  getSyncManager().updateOrAddPeriodicSync(info, runAtTime,                          flextime, extras); @@ -761,11 +769,8 @@ public final class ContentService extends IContentService.Stub {                  "no permission to write the sync settings");          int userId = UserHandle.getCallingUserId(); -        if (pollFrequency < 3600) { -            Slog.w(TAG, "Requested poll frequency of " + pollFrequency -                    + " seconds being rounded up to 1 hour."); -            pollFrequency = 3600; -        } + +        pollFrequency = clampPeriod(pollFrequency);          long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency);          long identityToken = clearCallingIdentity(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 96ced9615560..8520e045d575 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -102,11 +102,11 @@ import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCES  import android.Manifest;  import android.annotation.NonNull;  import android.annotation.Nullable; +import android.annotation.UserIdInt;  import android.app.ActivityManager;  import android.app.ActivityManagerNative;  import android.app.IActivityManager;  import android.app.ResourcesManager; -import android.app.admin.DevicePolicyManagerInternal;  import android.app.admin.IDevicePolicyManager;  import android.app.admin.SecurityLog;  import android.app.backup.IBackupManager; @@ -622,6 +622,8 @@ public class PackageManagerService extends IPackageManager.Stub {      @GuardedBy("mPackages")      final ArraySet<String> mFrozenPackages = new ArraySet<>(); +    final ProtectedPackages mProtectedPackages = new ProtectedPackages(); +      boolean mRestoredSettings;      // System configuration read by SystemConfig. @@ -16409,9 +16411,7 @@ public class PackageManagerService extends IPackageManager.Stub {          enforceCrossUserPermission(Binder.getCallingUid(), userId,                  true /* requireFullPermission */, false /* checkShell */, "clear application data"); -        final DevicePolicyManagerInternal dpmi = LocalServices -                .getService(DevicePolicyManagerInternal.class); -        if (dpmi != null && dpmi.hasDeviceOwnerOrProfileOwner(packageName, userId)) { +        if (mProtectedPackages.canPackageBeWiped(userId, packageName)) {              throw new SecurityException("Cannot clear data for a device owner or a profile owner");          }          // Queue up an async operation since the package deletion may take a little while. @@ -17738,10 +17738,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());                          + Binder.getCallingPid()                          + ", uid=" + uid + ", package uid=" + pkgSetting.appId);              } -            // Don't allow changing profile and device owners. Calling into DPMS, so no locking. -            final DevicePolicyManagerInternal dpmi = LocalServices -                    .getService(DevicePolicyManagerInternal.class); -            if (dpmi != null && dpmi.hasDeviceOwnerOrProfileOwner(packageName, userId)) { +            // Don't allow changing profile and device owners. +            if (mProtectedPackages.canPackageStateBeChanged(userId, packageName)) {                  throw new SecurityException("Cannot disable a device owner or a profile owner");              }          } @@ -20838,6 +20836,20 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());                  int userId) {              return PackageManagerService.this.getHomeActivitiesAsUser(allHomeCandidates, userId);          } + +        @Override +        public void setDeviceAndProfileOwnerPackages( +                int deviceOwnerUserId, String deviceOwnerPackage, +                SparseArray<String> profileOwnerPackages) { +            mProtectedPackages.setDeviceAndProfileOwnerPackages( +                    deviceOwnerUserId, deviceOwnerPackage, profileOwnerPackages); +        } + +        @Override +        public boolean canPackageBeWiped(int userId, String packageName) { +            return mProtectedPackages.canPackageBeWiped(userId, +                    packageName); +        }      }      @Override diff --git a/services/core/java/com/android/server/pm/ProtectedPackages.java b/services/core/java/com/android/server/pm/ProtectedPackages.java new file mode 100644 index 000000000000..7bdea181473e --- /dev/null +++ b/services/core/java/com/android/server/pm/ProtectedPackages.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.annotation.UserIdInt; +import android.os.UserHandle; +import android.util.SparseArray; + +/** + * Manages package names that need special protection. + * + * TODO: This class should persist the information by itself, and also keeps track of device admin + * packages for all users.  Then PMS.isPackageDeviceAdmin() should use it instead of talking + * to DPMS. + */ +public class ProtectedPackages { +    @UserIdInt +    private int mDeviceOwnerUserId; + +    private String mDeviceOwnerPackage; + +    private SparseArray<String> mProfileOwnerPackages; + +    private final Object mLock = new Object(); + +    /** +     * Sets the device/profile owner information. +     */ +    public void setDeviceAndProfileOwnerPackages( +            int deviceOwnerUserId, String deviceOwnerPackage, +            SparseArray<String> profileOwnerPackages) { +        synchronized (mLock) { +            mDeviceOwnerUserId = deviceOwnerUserId; +            mDeviceOwnerPackage = +                    (deviceOwnerUserId == UserHandle.USER_NULL) ? null : deviceOwnerPackage; +            mProfileOwnerPackages = (profileOwnerPackages == null) ? null +                    : profileOwnerPackages.clone(); +        } +    } + +    private boolean hasDeviceOwnerOrProfileOwner(int userId, String packageName) { +        if (packageName == null) { +            return false; +        } +        synchronized (mLock) { +            if (mDeviceOwnerPackage != null) { +                if ((mDeviceOwnerUserId == userId) +                        && (packageName.equals(mDeviceOwnerPackage))) { +                    return true; +                } +            } +            if (mProfileOwnerPackages != null) { +                if (packageName.equals(mProfileOwnerPackages.get(userId))) { +                    return true; +                } +            } +        } +        return false; +    } + +    /** +     * Whether a package or the components in a package's enabled state can be changed +     * by other callers than itself. +     */ +    public boolean canPackageStateBeChanged(@UserIdInt int userId, String packageName) { +        return hasDeviceOwnerOrProfileOwner(userId, packageName); +    } + +    /** +     * Whether a package's data be cleared. +     */ +    public boolean canPackageBeWiped(@UserIdInt int userId, String packageName) { +        return hasDeviceOwnerOrProfileOwner(userId, packageName); +    } +} diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java index 3700c71e1da7..b4c4bd8daa76 100644 --- a/services/core/java/com/android/server/vr/VrManagerService.java +++ b/services/core/java/com/android/server/vr/VrManagerService.java @@ -46,11 +46,12 @@ import android.service.vr.IVrListener;  import android.service.vr.IVrManager;  import android.service.vr.IVrStateCallbacks;  import android.service.vr.VrListenerService; +import android.util.ArrayMap;  import android.util.ArraySet;  import android.util.Slog;  import android.util.SparseArray; -  import com.android.internal.R; +import com.android.server.LocalServices;  import com.android.server.SystemConfig;  import com.android.server.SystemService;  import com.android.server.utils.ManagedApplicationService.PendingEvent; @@ -67,6 +68,7 @@ import java.util.ArrayList;  import java.util.Collection;  import java.util.Date;  import java.util.List; +import java.util.Map;  import java.util.Objects;  /** @@ -197,29 +199,44 @@ public class VrManagerService extends SystemService implements EnabledComponentC      private final class NotificationAccessManager {          private final SparseArray<ArraySet<String>> mAllowedPackages = new SparseArray<>(); +        private final ArrayMap<String, Integer> mNotificationAccessPackageToUserId = +                new ArrayMap<>();          public void update(Collection<String> packageNames) {              int currentUserId = ActivityManager.getCurrentUser(); -            UserHandle currentUserHandle = new UserHandle(currentUserId); -              ArraySet<String> allowed = mAllowedPackages.get(currentUserId);              if (allowed == null) {                  allowed = new ArraySet<>();              } +            // Make sure we revoke notification access for listeners in other users +            final int listenerCount = mNotificationAccessPackageToUserId.size(); +            for (int i = listenerCount - 1; i >= 0; i--) { +                final int grantUserId = mNotificationAccessPackageToUserId.valueAt(i); +                if (grantUserId != currentUserId) { +                    String packageName = mNotificationAccessPackageToUserId.keyAt(i); +                    revokeNotificationListenerAccess(packageName, grantUserId); +                    revokeNotificationPolicyAccess(packageName); +                    revokeCoarseLocationPermissionIfNeeded(packageName, grantUserId); +                    mNotificationAccessPackageToUserId.removeAt(i); +                } +            } +              for (String pkg : allowed) {                  if (!packageNames.contains(pkg)) { -                    revokeNotificationListenerAccess(pkg); +                    revokeNotificationListenerAccess(pkg, currentUserId);                      revokeNotificationPolicyAccess(pkg); -                    revokeCoarseLocationAccess(pkg, currentUserHandle); +                    revokeCoarseLocationPermissionIfNeeded(pkg, currentUserId); +                    mNotificationAccessPackageToUserId.remove(pkg);                  }              }              for (String pkg : packageNames) {                  if (!allowed.contains(pkg)) {                      grantNotificationPolicyAccess(pkg); -                    grantNotificationListenerAccess(pkg, currentUserHandle); -                    grantCoarseLocationAccess(pkg, currentUserHandle); +                    grantNotificationListenerAccess(pkg, currentUserId); +                    grantCoarseLocationPermissionIfNeeded(pkg, currentUserId); +                    mNotificationAccessPackageToUserId.put(pkg, currentUserId);                  }              } @@ -229,7 +246,6 @@ public class VrManagerService extends SystemService implements EnabledComponentC          }      } -      /**       * Called when a user, package, or setting changes that could affect whether or not the       * currently bound VrListenerService is changed. @@ -535,17 +551,33 @@ public class VrManagerService extends SystemService implements EnabledComponentC          }      } -    private void updateOverlayStateLocked(ComponentName exemptedComponent) { +    private void updateOverlayStateLocked(String exemptedPackage, int newUserId, int oldUserId) { +        AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class); + +        // If user changed drop restrictions for the old user. +        if (oldUserId != newUserId) { +            appOpsManager.setUserRestrictionForUser(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, +                    false, mOverlayToken, null, oldUserId); +        } + +        // Apply the restrictions for the current user based on vr state +        String[] exemptions = (exemptedPackage == null) ? new String[0] : +                new String[] { exemptedPackage }; + +        appOpsManager.setUserRestrictionForUser(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, +                mVrModeEnabled, mOverlayToken, exemptions, newUserId); +    } + +    private void updateDependentAppOpsLocked(String newVrServicePackage, int newUserId, +            String oldVrServicePackage, int oldUserId) { +        // If VR state changed and we also have a VR service change. +        if (Objects.equals(newVrServicePackage, oldVrServicePackage)) { +            return; +        }          final long identity = Binder.clearCallingIdentity();          try { -            AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class); -            if (appOpsManager != null) { -                String[] exemptions = (exemptedComponent == null) ? new String[0] : -                        new String[] { exemptedComponent.getPackageName() }; - -                appOpsManager.setUserRestriction(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, -                        mVrModeEnabled, mOverlayToken, exemptions); -            } +            // Set overlay exception state based on VR enabled and current service +            updateOverlayStateLocked(newVrServicePackage, newUserId, oldUserId);          } finally {              Binder.restoreCallingIdentity(identity);          } @@ -578,8 +610,12 @@ public class VrManagerService extends SystemService implements EnabledComponentC                  return validUserComponent; // Disabled -> Disabled transition does nothing.              } +            String oldVrServicePackage = mCurrentVrService != null +                    ? mCurrentVrService.getComponent().getPackageName() : null; +            final int oldUserId = mCurrentVrModeUser; +              // Always send mode change events. -            changeVrModeLocked(enabled, (enabled && validUserComponent) ? component : null); +            changeVrModeLocked(enabled);              if (!enabled || !validUserComponent) {                  // Unbind whatever is running @@ -606,12 +642,25 @@ public class VrManagerService extends SystemService implements EnabledComponentC                  }              } -            if (calling != null && !Objects.equals(calling, mCurrentVrModeComponent))  { +            if (calling != null && !Objects.equals(calling, mCurrentVrModeComponent)) {                  mCurrentVrModeComponent = calling; +                sendUpdatedCaller = true; +            } + +            if (mCurrentVrModeUser != userId) {                  mCurrentVrModeUser = userId;                  sendUpdatedCaller = true;              } +            String newVrServicePackage = mCurrentVrService != null +                    ? mCurrentVrService.getComponent().getPackageName() : null; +            final int newUserId = mCurrentVrModeUser; + +            // Update AppOps settings that change state when entering/exiting VR mode, or changing +            // the current VrListenerService. +            updateDependentAppOpsLocked(newVrServicePackage, newUserId, +                    oldVrServicePackage, oldUserId); +              if (mCurrentVrService != null && sendUpdatedCaller) {                  final ComponentName c = mCurrentVrModeComponent;                  mCurrentVrService.sendEvent(new PendingEvent() { @@ -645,18 +694,6 @@ public class VrManagerService extends SystemService implements EnabledComponentC          return true;      } -    private void grantCoarseLocationAccess(String pkg, UserHandle userId) { -        PackageManager pm = mContext.getPackageManager(); -        pm.grantRuntimePermission(pkg, android.Manifest.permission.ACCESS_COARSE_LOCATION, -                userId); -    } - -    private void revokeCoarseLocationAccess(String pkg, UserHandle userId) { -        PackageManager pm = mContext.getPackageManager(); -        pm.revokeRuntimePermission(pkg, -                android.Manifest.permission.ACCESS_COARSE_LOCATION, userId); -    } -      private void grantNotificationPolicyAccess(String pkg) {          NotificationManager nm = mContext.getSystemService(NotificationManager.class);          nm.setNotificationPolicyAccessGranted(pkg, true); @@ -670,14 +707,14 @@ public class VrManagerService extends SystemService implements EnabledComponentC          nm.setNotificationPolicyAccessGranted(pkg, false);      } -    private void grantNotificationListenerAccess(String pkg, UserHandle userId) { +    private void grantNotificationListenerAccess(String pkg, int userId) {          PackageManager pm = mContext.getPackageManager();          ArraySet<ComponentName> possibleServices = EnabledComponentsObserver.loadComponentNames(pm, -                userId.getIdentifier(), NotificationListenerService.SERVICE_INTERFACE, +                userId, NotificationListenerService.SERVICE_INTERFACE,                  android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE);          ContentResolver resolver = mContext.getContentResolver(); -        ArraySet<String> current = getCurrentNotifListeners(resolver); +        ArraySet<String> current = getNotificationListeners(resolver, userId);          for (ComponentName c : possibleServices) {              String flatName = c.flattenToString(); @@ -689,14 +726,16 @@ public class VrManagerService extends SystemService implements EnabledComponentC          if (current.size() > 0) {              String flatSettings = formatSettings(current); -            Settings.Secure.putString(resolver, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, -                    flatSettings); +            Settings.Secure.putStringForUser(resolver, +                    Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, +                    flatSettings, userId);          }      } -    private void revokeNotificationListenerAccess(String pkg) { +    private void revokeNotificationListenerAccess(String pkg, int userId) {          ContentResolver resolver = mContext.getContentResolver(); -        ArraySet<String> current = getCurrentNotifListeners(resolver); + +        ArraySet<String> current = getNotificationListeners(resolver, userId);          ArrayList<String> toRemove = new ArrayList<>(); @@ -710,14 +749,37 @@ public class VrManagerService extends SystemService implements EnabledComponentC          current.removeAll(toRemove);          String flatSettings = formatSettings(current); -        Settings.Secure.putString(resolver, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, -                flatSettings); +        Settings.Secure.putStringForUser(resolver, +                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, +                flatSettings, userId); +    } + +    private void grantCoarseLocationPermissionIfNeeded(String pkg, int userId) { +        // Don't clobber the user if permission set in current state explicitly +        if (!isPermissionUserUpdated(Manifest.permission.ACCESS_COARSE_LOCATION, pkg, userId)) { +            mContext.getPackageManager().grantRuntimePermission(pkg, +                    Manifest.permission.ACCESS_COARSE_LOCATION, new UserHandle(userId)); +        } +    } + +    private void revokeCoarseLocationPermissionIfNeeded(String pkg, int userId) { +        // Don't clobber the user if permission set in current state explicitly +        if (!isPermissionUserUpdated(Manifest.permission.ACCESS_COARSE_LOCATION, pkg, userId)) { +            mContext.getPackageManager().revokeRuntimePermission(pkg, +                    Manifest.permission.ACCESS_COARSE_LOCATION, new UserHandle(userId)); +        } +    } +    private boolean isPermissionUserUpdated(String permission, String pkg, int userId) { +        final int flags = mContext.getPackageManager().getPermissionFlags( +                permission, pkg, new UserHandle(userId)); +        return (flags & (PackageManager.FLAG_PERMISSION_USER_SET +                | PackageManager.FLAG_PERMISSION_USER_FIXED)) != 0;      } -    private ArraySet<String> getCurrentNotifListeners(ContentResolver resolver) { -        String flat = Settings.Secure.getString(resolver, -                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); +    private ArraySet<String> getNotificationListeners(ContentResolver resolver, int userId) { +        String flat = Settings.Secure.getStringForUser(resolver, +                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, userId);          ArraySet<String> current = new ArraySet<>();          if (flat != null) { @@ -763,9 +825,8 @@ public class VrManagerService extends SystemService implements EnabledComponentC       * Note: Must be called while holding {@code mLock}.       *       * @param enabled new state of the VR mode. -     * @param exemptedComponent a component to exempt from AppOps restrictions for overlays.       */ -    private void changeVrModeLocked(boolean enabled, ComponentName exemptedComponent) { +    private void changeVrModeLocked(boolean enabled) {          if (mVrModeEnabled != enabled) {              mVrModeEnabled = enabled; @@ -773,7 +834,6 @@ public class VrManagerService extends SystemService implements EnabledComponentC              Slog.i(TAG, "VR mode " + ((mVrModeEnabled) ? "enabled" : "disabled"));              setVrModeNative(mVrModeEnabled); -            updateOverlayStateLocked(exemptedComponent);              onVrModeChangedLocked();          }      } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 9a7e64b3a923..b5280165bb39 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1338,7 +1338,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          }          Owners newOwners() { -            return new Owners(mContext, getUserManager(), getUserManagerInternal()); +            return new Owners(getUserManager(), getUserManagerInternal(), +                    getPackageManagerInternal());          }          UserManager getUserManager() { @@ -8134,45 +8135,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          }          @Override -        public boolean hasDeviceOwnerOrProfileOwner(String packageName, int userId) { -            if (!mHasFeature || packageName == null) { -                return false; -            } -            if (userId < 0) { -                throw new UnsupportedOperationException("userId should be >= 0"); -            } -            synchronized (DevicePolicyManagerService.this) { -                if (packageName.equals(mOwners.getProfileOwnerPackage(userId))) { -                    return true; -                } -                if (userId == mOwners.getDeviceOwnerUserId() -                        && packageName.equals(mOwners.getDeviceOwnerPackageName())) { -                    return true; -                } -            } -            return false; -        } - -        @Override          public Intent createPackageSuspendedDialogIntent(String packageName, int userId) {              Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);              intent.putExtra(Intent.EXTRA_USER_ID, userId);              intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); -            synchronized (DevicePolicyManagerService.this) { -                ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId); -                if (profileOwner != null) { -                    intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, profileOwner); -                    return intent; -                } -                if (mOwners.getDeviceOwnerUserId() == userId) { -                    ComponentName deviceOwner = mOwners.getDeviceOwnerComponent(); -                    if (deviceOwner != null) { -                        intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, deviceOwner); -                        return intent; -                    } -                } +            // This method is called from AM with its lock held, so don't take the DPMS lock. +            // b/29242568 + +            ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId); +            if (profileOwner != null) { +                intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, profileOwner); +                return intent;              } + +            final Pair<Integer, ComponentName> deviceOwner = +                    mOwners.getDeviceOwnerUserIdAndComponent(); +            if (deviceOwner != null && deviceOwner.first == userId) { +                intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, deviceOwner.second); +                return intent; +            } +              // We're not specifying the device admin because there isn't one.              return intent;          } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index b316cbd96453..1ae1a773b23e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -16,9 +16,10 @@  package com.android.server.devicepolicy; +import android.annotation.Nullable;  import android.app.admin.SystemUpdatePolicy;  import android.content.ComponentName; -import android.content.Context; +import android.content.pm.PackageManagerInternal;  import android.content.pm.UserInfo;  import android.os.Environment;  import android.os.UserHandle; @@ -27,7 +28,9 @@ import android.os.UserManagerInternal;  import android.util.ArrayMap;  import android.util.AtomicFile;  import android.util.Log; +import android.util.Pair;  import android.util.Slog; +import android.util.SparseArray;  import android.util.Xml;  import com.android.internal.util.FastXmlSerializer; @@ -52,8 +55,8 @@ import libcore.io.IoUtils;   * Stores and restores state for the Device and Profile owners. By definition there can be   * only one device owner, but there may be a profile owner for each user.   * - * <p>This class is not thread safe.  (i.e. access to this class must always be synchronized - * in the caller side.) + * <p>This class is thread safe, so individual methods can safely be called without locking. + * However, caller must still synchronize on their side to ensure integrity between multiple calls.   */  class Owners {      private static final String TAG = "DevicePolicyManagerService"; @@ -86,6 +89,7 @@ class Owners {      private final UserManager mUserManager;      private final UserManagerInternal mUserManagerInternal; +    private final PackageManagerInternal mPackageManagerInternal;      // Internal state for the device owner package.      private OwnerInfo mDeviceOwner; @@ -98,77 +102,117 @@ class Owners {      // Local system update policy controllable by device owner.      private SystemUpdatePolicy mSystemUpdatePolicy; -    public Owners(Context context, UserManager userManager, -            UserManagerInternal userManagerInternal) { +    private final Object mLock = new Object(); + +    public Owners(UserManager userManager, +            UserManagerInternal userManagerInternal, +            PackageManagerInternal packageManagerInternal) {          mUserManager = userManager;          mUserManagerInternal = userManagerInternal; +        mPackageManagerInternal = packageManagerInternal;      }      /**       * Load configuration from the disk.       */      void load() { -        // First, try to read from the legacy file. -        final File legacy = getLegacyConfigFileWithTestOverride(); +        synchronized (mLock) { +            // First, try to read from the legacy file. +            final File legacy = getLegacyConfigFileWithTestOverride(); -        final List<UserInfo> users = mUserManager.getUsers(); +            final List<UserInfo> users = mUserManager.getUsers(); -        if (readLegacyOwnerFile(legacy)) { -            if (DEBUG) { -                Log.d(TAG, "Legacy config file found."); -            } +            if (readLegacyOwnerFileLocked(legacy)) { +                if (DEBUG) { +                    Log.d(TAG, "Legacy config file found."); +                } -            // Legacy file exists, write to new files and remove the legacy one. -            writeDeviceOwner(); -            for (int userId : getProfileOwnerKeys()) { -                writeProfileOwner(userId); -            } -            if (DEBUG) { -                Log.d(TAG, "Deleting legacy config file"); -            } -            if (!legacy.delete()) { -                Slog.e(TAG, "Failed to remove the legacy setting file"); -            } -        } else { -            // No legacy file, read from the new format files. -            new DeviceOwnerReadWriter().readFromFileLocked(); +                // Legacy file exists, write to new files and remove the legacy one. +                writeDeviceOwner(); +                for (int userId : getProfileOwnerKeys()) { +                    writeProfileOwner(userId); +                } +                if (DEBUG) { +                    Log.d(TAG, "Deleting legacy config file"); +                } +                if (!legacy.delete()) { +                    Slog.e(TAG, "Failed to remove the legacy setting file"); +                } +            } else { +                // No legacy file, read from the new format files. +                new DeviceOwnerReadWriter().readFromFileLocked(); +                for (UserInfo ui : users) { +                    new ProfileOwnerReadWriter(ui.id).readFromFileLocked(); +                } +            } +            mUserManagerInternal.setDeviceManaged(hasDeviceOwner());              for (UserInfo ui : users) { -                new ProfileOwnerReadWriter(ui.id).readFromFileLocked(); +                mUserManagerInternal.setUserManaged(ui.id, hasProfileOwner(ui.id));              } +            if (hasDeviceOwner() && hasProfileOwner(getDeviceOwnerUserId())) { +                Slog.w(TAG, String.format("User %d has both DO and PO, which is not supported", +                        getDeviceOwnerUserId())); +            } +            pushToPackageManagerLocked();          } -        mUserManagerInternal.setDeviceManaged(hasDeviceOwner()); -        for (UserInfo ui : users) { -            mUserManagerInternal.setUserManaged(ui.id, hasProfileOwner(ui.id)); -        } -        if (hasDeviceOwner() && hasProfileOwner(getDeviceOwnerUserId())) { -            Slog.w(TAG, String.format("User %d has both DO and PO, which is not supported", -                    getDeviceOwnerUserId())); +    } + +    private void pushToPackageManagerLocked() { +        final SparseArray<String> po = new SparseArray<>(); +        for (int i = mProfileOwners.size() - 1; i >= 0; i--) { +            po.put(mProfileOwners.keyAt(i), mProfileOwners.valueAt(i).packageName);          } +        mPackageManagerInternal.setDeviceAndProfileOwnerPackages( +                mDeviceOwnerUserId, (mDeviceOwner != null ? mDeviceOwner.packageName : null), +                po);      }      String getDeviceOwnerPackageName() { -        return mDeviceOwner != null ? mDeviceOwner.packageName : null; +        synchronized (mLock) { +            return mDeviceOwner != null ? mDeviceOwner.packageName : null; +        }      }      int getDeviceOwnerUserId() { -        return mDeviceOwnerUserId; +        synchronized (mLock) { +            return mDeviceOwnerUserId; +        } +    } + +    @Nullable +    Pair<Integer, ComponentName> getDeviceOwnerUserIdAndComponent() { +        synchronized (mLock) { +            if (mDeviceOwner == null) { +                return null; +            } else { +                return Pair.create(mDeviceOwnerUserId, mDeviceOwner.admin); +            } +        }      }      String getDeviceOwnerName() { -        return mDeviceOwner != null ? mDeviceOwner.name : null; +        synchronized (mLock) { +            return mDeviceOwner != null ? mDeviceOwner.name : null; +        }      }      ComponentName getDeviceOwnerComponent() { -        return mDeviceOwner != null ? mDeviceOwner.admin : null; +        synchronized (mLock) { +            return mDeviceOwner != null ? mDeviceOwner.admin : null; +        }      }      String getDeviceOwnerRemoteBugreportUri() { -        return mDeviceOwner != null ? mDeviceOwner.remoteBugreportUri : null; +        synchronized (mLock) { +            return mDeviceOwner != null ? mDeviceOwner.remoteBugreportUri : null; +        }      }      String getDeviceOwnerRemoteBugreportHash() { -        return mDeviceOwner != null ? mDeviceOwner.remoteBugreportHash : null; +        synchronized (mLock) { +            return mDeviceOwner != null ? mDeviceOwner.remoteBugreportHash : null; +        }      }      void setDeviceOwner(ComponentName admin, String ownerName, int userId) { @@ -176,128 +220,172 @@ class Owners {              Slog.e(TAG, "Invalid user id for device owner user: " + userId);              return;          } -        // For a newly set DO, there's no need for migration. -        setDeviceOwnerWithRestrictionsMigrated(admin, ownerName, userId, -                /* userRestrictionsMigrated =*/ true); +        synchronized (mLock) { +            // For a newly set DO, there's no need for migration. +            setDeviceOwnerWithRestrictionsMigrated(admin, ownerName, userId, +                    /* userRestrictionsMigrated =*/ true); +        }      }      // Note this should be only called during migration.  Normally when DO is set,      // userRestrictionsMigrated should always be true.      void setDeviceOwnerWithRestrictionsMigrated(ComponentName admin, String ownerName, int userId,              boolean userRestrictionsMigrated) { -        mDeviceOwner = new OwnerInfo(ownerName, admin, userRestrictionsMigrated, -                /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/ null); -        mDeviceOwnerUserId = userId; +        synchronized (mLock) { +            mDeviceOwner = new OwnerInfo(ownerName, admin, userRestrictionsMigrated, +                    /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/ null); +            mDeviceOwnerUserId = userId; -        mUserManagerInternal.setDeviceManaged(true); +            mUserManagerInternal.setDeviceManaged(true); +            pushToPackageManagerLocked(); +        }      }      void clearDeviceOwner() { -        mDeviceOwner = null; -        mDeviceOwnerUserId = UserHandle.USER_NULL; +        synchronized (mLock) { +            mDeviceOwner = null; +            mDeviceOwnerUserId = UserHandle.USER_NULL; -        mUserManagerInternal.setDeviceManaged(false); +            mUserManagerInternal.setDeviceManaged(false); +            pushToPackageManagerLocked(); +        }      }      void setProfileOwner(ComponentName admin, String ownerName, int userId) { -        // For a newly set PO, there's no need for migration. -        mProfileOwners.put(userId, new OwnerInfo(ownerName, admin, -                /* userRestrictionsMigrated =*/ true, /* remoteBugreportUri =*/ null, -                /* remoteBugreportHash =*/ null)); -        mUserManagerInternal.setUserManaged(userId, true); +        synchronized (mLock) { +            // For a newly set PO, there's no need for migration. +            mProfileOwners.put(userId, new OwnerInfo(ownerName, admin, +                    /* userRestrictionsMigrated =*/ true, /* remoteBugreportUri =*/ null, +                    /* remoteBugreportHash =*/ null)); +            mUserManagerInternal.setUserManaged(userId, true); +            pushToPackageManagerLocked(); +        }      }      void removeProfileOwner(int userId) { -        mProfileOwners.remove(userId); -        mUserManagerInternal.setUserManaged(userId, false); +        synchronized (mLock) { +            mProfileOwners.remove(userId); +            mUserManagerInternal.setUserManaged(userId, false); +            pushToPackageManagerLocked(); +        }      }      ComponentName getProfileOwnerComponent(int userId) { -        OwnerInfo profileOwner = mProfileOwners.get(userId); -        return profileOwner != null ? profileOwner.admin : null; +        synchronized (mLock) { +            OwnerInfo profileOwner = mProfileOwners.get(userId); +            return profileOwner != null ? profileOwner.admin : null; +        }      }      String getProfileOwnerName(int userId) { -        OwnerInfo profileOwner = mProfileOwners.get(userId); -        return profileOwner != null ? profileOwner.name : null; +        synchronized (mLock) { +            OwnerInfo profileOwner = mProfileOwners.get(userId); +            return profileOwner != null ? profileOwner.name : null; +        }      }      String getProfileOwnerPackage(int userId) { -        OwnerInfo profileOwner = mProfileOwners.get(userId); -        return profileOwner != null ? profileOwner.packageName : null; +        synchronized (mLock) { +            OwnerInfo profileOwner = mProfileOwners.get(userId); +            return profileOwner != null ? profileOwner.packageName : null; +        }      }      Set<Integer> getProfileOwnerKeys() { -        return mProfileOwners.keySet(); +        synchronized (mLock) { +            return mProfileOwners.keySet(); +        }      }      SystemUpdatePolicy getSystemUpdatePolicy() { -        return mSystemUpdatePolicy; +        synchronized (mLock) { +            return mSystemUpdatePolicy; +        }      }      void setSystemUpdatePolicy(SystemUpdatePolicy systemUpdatePolicy) { -        mSystemUpdatePolicy = systemUpdatePolicy; +        synchronized (mLock) { +            mSystemUpdatePolicy = systemUpdatePolicy; +        }      }      void clearSystemUpdatePolicy() { -        mSystemUpdatePolicy = null; +        synchronized (mLock) { +            mSystemUpdatePolicy = null; +        }      }      boolean hasDeviceOwner() { -        return mDeviceOwner != null; +        synchronized (mLock) { +            return mDeviceOwner != null; +        }      }      boolean isDeviceOwnerUserId(int userId) { -        return mDeviceOwner != null && mDeviceOwnerUserId == userId; +        synchronized (mLock) { +            return mDeviceOwner != null && mDeviceOwnerUserId == userId; +        }      }      boolean hasProfileOwner(int userId) { -        return getProfileOwnerComponent(userId) != null; +        synchronized (mLock) { +            return getProfileOwnerComponent(userId) != null; +        }      }      /**       * @return true if user restrictions need to be migrated for DO.       */      boolean getDeviceOwnerUserRestrictionsNeedsMigration() { -        return mDeviceOwner != null && !mDeviceOwner.userRestrictionsMigrated; +        synchronized (mLock) { +            return mDeviceOwner != null && !mDeviceOwner.userRestrictionsMigrated; +        }      }      /**       * @return true if user restrictions need to be migrated for PO.       */      boolean getProfileOwnerUserRestrictionsNeedsMigration(int userId) { -        OwnerInfo profileOwner = mProfileOwners.get(userId); -        return profileOwner != null && !profileOwner.userRestrictionsMigrated; +        synchronized (mLock) { +            OwnerInfo profileOwner = mProfileOwners.get(userId); +            return profileOwner != null && !profileOwner.userRestrictionsMigrated; +        }      }      /** Sets the user restrictions migrated flag, and also writes to the file. */      void setDeviceOwnerUserRestrictionsMigrated() { -        if (mDeviceOwner != null) { -            mDeviceOwner.userRestrictionsMigrated = true; +        synchronized (mLock) { +            if (mDeviceOwner != null) { +                mDeviceOwner.userRestrictionsMigrated = true; +            } +            writeDeviceOwner();          } -        writeDeviceOwner();      }      /** Sets the remote bugreport uri and hash, and also writes to the file. */      void setDeviceOwnerRemoteBugreportUriAndHash(String remoteBugreportUri,              String remoteBugreportHash) { -        if (mDeviceOwner != null) { -            mDeviceOwner.remoteBugreportUri = remoteBugreportUri; -            mDeviceOwner.remoteBugreportHash = remoteBugreportHash; +        synchronized (mLock) { +            if (mDeviceOwner != null) { +                mDeviceOwner.remoteBugreportUri = remoteBugreportUri; +                mDeviceOwner.remoteBugreportHash = remoteBugreportHash; +            } +            writeDeviceOwner();          } -        writeDeviceOwner();      }      /** Sets the user restrictions migrated flag, and also writes to the file.  */      void setProfileOwnerUserRestrictionsMigrated(int userId) { -        OwnerInfo profileOwner = mProfileOwners.get(userId); -        if (profileOwner != null) { -            profileOwner.userRestrictionsMigrated = true; +        synchronized (mLock) { +            OwnerInfo profileOwner = mProfileOwners.get(userId); +            if (profileOwner != null) { +                profileOwner.userRestrictionsMigrated = true; +            } +            writeProfileOwner(userId);          } -        writeProfileOwner(userId);      } -    private boolean readLegacyOwnerFile(File file) { +    private boolean readLegacyOwnerFileLocked(File file) {          if (!file.exists()) {              // Already migrated or the device has no owners.              return false; @@ -363,7 +451,7 @@ class Owners {      }      void writeDeviceOwner() { -        synchronized (this) { +        synchronized (mLock) {              if (DEBUG) {                  Log.d(TAG, "Writing to device owner file");              } @@ -372,7 +460,7 @@ class Owners {      }      void writeProfileOwner(int userId) { -        synchronized (this) { +        synchronized (mLock) {              if (DEBUG) {                  Log.d(TAG, "Writing to profile owner file for user " + userId);              } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 107de8be496f..b21f5fb4b70f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -181,7 +181,7 @@ public final class SystemServer {       * visual content.       */      private static final int DEFAULT_SYSTEM_THEME = -            com.android.internal.R.style.Theme_DeviceDefault_Light_DarkActionBar; +            com.android.internal.R.style.Theme_DeviceDefault_System;      private final int mFactoryTestMode;      private Timer mProfilerSnapshotTimer; diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java index 744443f1d7e7..6cb4a8238727 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -28,10 +28,8 @@ import android.os.PowerManagerInternal;  import android.os.UserHandle;  import android.os.UserManager;  import android.os.UserManagerInternal; -import android.os.storage.StorageManager;  import android.telephony.TelephonyManager;  import android.util.ArrayMap; -import android.util.Log;  import android.util.Pair;  import android.view.IWindowManager; @@ -57,7 +55,7 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi          private final File mProfileOwnerBase;          public OwnersTestable(DpmMockContext context) { -            super(context, context.userManager, context.userManagerInternal); +            super(context.userManager, context.userManagerInternal, context.packageManagerInternal);              mLegacyFile = new File(context.dataDir, LEGACY_FILE);              mDeviceOwnerFile = new File(context.dataDir, DEVICE_OWNER_FILE);              mProfileOwnerBase = new File(context.dataDir, PROFILE_OWNER_FILE_BASE); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java index 0f68cca04f30..50c5d5d7b2b8 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java @@ -40,11 +40,12 @@ public class DatabaseHelper extends SQLiteOpenHelper {      static final boolean DBG = false;      private static final String NAME = "sound_model.db"; -    private static final int VERSION = 4; +    private static final int VERSION = 5;      public static interface SoundModelContract {          public static final String TABLE = "sound_model";          public static final String KEY_MODEL_UUID = "model_uuid"; +        public static final String KEY_VENDOR_UUID = "vendor_uuid";          public static final String KEY_KEYPHRASE_ID = "keyphrase_id";          public static final String KEY_TYPE = "type";          public static final String KEY_DATA = "data"; @@ -58,6 +59,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {      private static final String CREATE_TABLE_SOUND_MODEL = "CREATE TABLE "              + SoundModelContract.TABLE + "("              + SoundModelContract.KEY_MODEL_UUID + " TEXT PRIMARY KEY," +            + SoundModelContract.KEY_VENDOR_UUID + " TEXT, "              + SoundModelContract.KEY_KEYPHRASE_ID + " INTEGER,"              + SoundModelContract.KEY_TYPE + " INTEGER,"              + SoundModelContract.KEY_DATA + " BLOB," @@ -78,9 +80,19 @@ public class DatabaseHelper extends SQLiteOpenHelper {      @Override      public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { -        // TODO: For now, drop older tables and recreate new ones. -        db.execSQL("DROP TABLE IF EXISTS " + SoundModelContract.TABLE); -        onCreate(db); +        if (oldVersion < 4) { +            // For old versions just drop the tables and recreate new ones. +            db.execSQL("DROP TABLE IF EXISTS " + SoundModelContract.TABLE); +            onCreate(db); +        } else { +            // In the jump to version 5, we added support for the vendor UUID. +            if (oldVersion == 4) { +                Slog.d(TAG, "Adding vendor UUID column"); +                db.execSQL("ALTER TABLE " + SoundModelContract.TABLE + " ADD COLUMN " +                        + SoundModelContract.KEY_VENDOR_UUID + " TEXT"); +                oldVersion++; +            } +        }      }      /** @@ -93,6 +105,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {              SQLiteDatabase db = getWritableDatabase();              ContentValues values = new ContentValues();              values.put(SoundModelContract.KEY_MODEL_UUID, soundModel.uuid.toString()); +            values.put(SoundModelContract.KEY_VENDOR_UUID, soundModel.vendorUuid.toString());              values.put(SoundModelContract.KEY_TYPE, SoundTrigger.SoundModel.TYPE_KEYPHRASE);              values.put(SoundModelContract.KEY_DATA, soundModel.data); @@ -176,6 +189,11 @@ public class DatabaseHelper extends SQLiteOpenHelper {                              continue;                          } +                        String vendorUuidString = null; +                        int vendorUuidColumn = c.getColumnIndex(SoundModelContract.KEY_VENDOR_UUID); +                        if (vendorUuidColumn != -1) { +                            vendorUuidString = c.getString(vendorUuidColumn); +                        }                          byte[] data = c.getBlob(c.getColumnIndex(SoundModelContract.KEY_DATA));                          int recognitionModes = c.getInt(                                  c.getColumnIndex(SoundModelContract.KEY_RECOGNITION_MODES)); @@ -212,9 +230,12 @@ public class DatabaseHelper extends SQLiteOpenHelper {                          Keyphrase[] keyphrases = new Keyphrase[1];                          keyphrases[0] = new Keyphrase(                                  keyphraseId, recognitionModes, modelLocale, text, users); +                        UUID vendorUuid = null; +                        if (vendorUuidString != null) { +                            vendorUuid = UUID.fromString(vendorUuidString); +                        }                          KeyphraseSoundModel model = new KeyphraseSoundModel( -                                UUID.fromString(modelUuid), -                                null /* FIXME use vendor UUID */, data, keyphrases); +                                UUID.fromString(modelUuid), vendorUuid, data, keyphrases);                          if (DBG) {                              Slog.d(TAG, "Found SoundModel for the given keyphrase/locale/user: "                                      + model); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index af15c0a829a5..44d89c14486b 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -759,6 +759,24 @@ public class CarrierConfigManager {      public static final String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT =              "duration_blocking_disabled_after_emergency_int"; +    /** +     * @hide +     * The default value for preferred CDMA roaming mode (aka CDMA system select.) +     *          CDMA_ROAMING_MODE_RADIO_DEFAULT = the default roaming mode from the radio +     *          CDMA_ROAMING_MODE_HOME = Home Networks +     *          CDMA_ROAMING_MODE_AFFILIATED = Roaming on Affiliated networks +     *          CDMA_ROAMING_MODE_ANY = Roaming on any networks +     */ +    public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int"; +    /** @hide */ +    public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1; +    /** @hide */ +    public static final int CDMA_ROAMING_MODE_HOME = 0; +    /** @hide */ +    public static final int CDMA_ROAMING_MODE_AFFILIATED = 1; +    /** @hide */ +    public static final int CDMA_ROAMING_MODE_ANY = 2; +      /** The default value for every variable. */      private final static PersistableBundle sDefaults; @@ -897,6 +915,7 @@ public class CarrierConfigManager {          sDefaults.putString(KEY_MMS_USER_AGENT_STRING, "");          sDefaults.putBoolean(KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL, true);          sDefaults.putBoolean(KEY_USE_RCS_PRESENCE_BOOL, false); +        sDefaults.putInt(KEY_CDMA_ROAMING_MODE_INT, CDMA_ROAMING_MODE_RADIO_DEFAULT);          // Carrier Signalling Receivers          sDefaults.putStringArray(KEY_SIGNAL_REDIRECTION_RECEIVER_STRING_ARRAY, null); |