diff options
54 files changed, 1246 insertions, 660 deletions
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java index 2754f2d270bd..6ce29cb24bc6 100644 --- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java +++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java @@ -93,7 +93,7 @@ public class PowerCommand extends Svc.Command { } else if ("shutdown".equals(args[1])) { try { // no confirm, wait till device is off - pm.shutdown(false, true); + pm.shutdown(false, null, true); } catch (RemoteException e) { System.err.println("Failed to shutdown."); } diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java index aa1be9a6a946..d331c2a066c4 100644 --- a/core/java/android/animation/Animator.java +++ b/core/java/android/animation/Animator.java @@ -27,6 +27,11 @@ import java.util.ArrayList; public abstract class Animator implements Cloneable { /** + * The value used to indicate infinite duration (e.g. when Animators repeat infinitely). + * @hide + */ + public static final long DURATION_INFINITE = -1; + /** * The set of listeners to be sent events through the life of an animation. */ ArrayList<AnimatorListener> mListeners = null; @@ -184,6 +189,16 @@ public abstract class Animator implements Cloneable { public abstract long getDuration(); /** + * Gets the total duration of the animation, accounting for animation sequences, start delay, + * and repeating. Return {@link #DURATION_INFINITE} if the duration is infinite. + * @hide + * TODO: Unhide + */ + public long getTotalDuration() { + return getStartDelay() + getDuration(); + } + + /** * The time interpolator used in calculating the elapsed fraction of the * animation. The interpolator determines whether the animation runs with * linear or non-linear motion, such as acceleration and deceleration. The diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index 951fe49cbd9e..5480193a8c34 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -17,6 +17,7 @@ package android.animation; import android.util.ArrayMap; +import android.util.Log; import java.util.ArrayList; import java.util.Collection; @@ -50,6 +51,7 @@ import java.util.List; */ public final class AnimatorSet extends Animator { + private static final String TAG = "AnimatorSet"; /** * Internal variables * NOTE: This object implements the clone() method, making a deep copy of any referenced @@ -79,20 +81,10 @@ public final class AnimatorSet extends Animator { private ArrayList<Node> mNodes = new ArrayList<Node>(); /** - * The sorted list of nodes. This is the order in which the animations will - * be played. The details about when exactly they will be played depend - * on the dependency relationships of the nodes. + * Animator Listener that tracks the lifecycle of each Animator in the set. It will be added + * to each Animator before they start and removed after they end. */ - private ArrayList<Node> mSortedNodes = new ArrayList<Node>(); - - /** - * Flag indicating whether the nodes should be sorted prior to playing. This - * flag allows us to cache the previous sorted nodes so that if the sequence - * is replayed with no changes, it does not have to re-sort the nodes again. - */ - private boolean mNeedsSort = true; - - private AnimatorSetListener mSetListener = null; + private AnimatorSetListener mSetListener = new AnimatorSetListener(this); /** * Flag indicating that the AnimatorSet has been manually @@ -101,7 +93,13 @@ public final class AnimatorSet extends Animator { * child animations of this AnimatorSet end. It also determines whether cancel/end * notifications are sent out via the normal AnimatorSetListener mechanism. */ - boolean mTerminated = false; + private boolean mTerminated = false; + + /** + * Tracks whether any change has been made to the AnimatorSet, which is then used to + * determine whether the dependency graph should be re-constructed. + */ + private boolean mDependencyDirty = false; /** * Indicates whether an AnimatorSet has been start()'d, whether or @@ -113,8 +111,13 @@ public final class AnimatorSet extends Animator { private long mStartDelay = 0; // Animator used for a nonzero startDelay - private ValueAnimator mDelayAnim = null; + private ValueAnimator mDelayAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(0); + // Root of the dependency tree of all the animators in the set. In this tree, parent-child + // relationship captures the order of animation (i.e. parent and child will play sequentially), + // and sibling relationship indicates "with" relationship, as sibling animators start at the + // same time. + private Node mRootNode = new Node(mDelayAnim); // How long the child animations should last in ms. The default value is negative, which // simply means that there is no duration set on the AnimatorSet. When a real duration is @@ -125,7 +128,17 @@ public final class AnimatorSet extends Animator { // was set on this AnimatorSet, so it should not be passed down to the children. private TimeInterpolator mInterpolator = null; + // Whether the AnimatorSet can be reversed. private boolean mReversible = true; + // The total duration of finishing all the Animators in the set. + private long mTotalDuration = 0; + + public AnimatorSet() { + super(); + mNodeMap.put(mDelayAnim, mRootNode); + mNodes.add(mRootNode); + } + /** * Sets up this AnimatorSet to play all of the supplied animations at the same time. * This is equivalent to calling {@link #play(Animator)} with the first animator in the @@ -139,7 +152,6 @@ public final class AnimatorSet extends Animator { */ public void playTogether(Animator... items) { if (items != null) { - mNeedsSort = true; Builder builder = play(items[0]); for (int i = 1; i < items.length; ++i) { builder.with(items[i]); @@ -154,7 +166,6 @@ public final class AnimatorSet extends Animator { */ public void playTogether(Collection<Animator> items) { if (items != null && items.size() > 0) { - mNeedsSort = true; Builder builder = null; for (Animator anim : items) { if (builder == null) { @@ -174,13 +185,12 @@ public final class AnimatorSet extends Animator { */ public void playSequentially(Animator... items) { if (items != null) { - mNeedsSort = true; if (items.length == 1) { play(items[0]); } else { mReversible = false; for (int i = 0; i < items.length - 1; ++i) { - play(items[i]).before(items[i+1]); + play(items[i]).before(items[i + 1]); } } } @@ -194,13 +204,12 @@ public final class AnimatorSet extends Animator { */ public void playSequentially(List<Animator> items) { if (items != null && items.size() > 0) { - mNeedsSort = true; if (items.size() == 1) { play(items.get(0)); } else { mReversible = false; for (int i = 0; i < items.size() - 1; ++i) { - play(items.get(i)).before(items.get(i+1)); + play(items.get(i)).before(items.get(i + 1)); } } } @@ -216,8 +225,10 @@ public final class AnimatorSet extends Animator { */ public ArrayList<Animator> getChildAnimations() { ArrayList<Animator> childList = new ArrayList<Animator>(); - for (Node node : mNodes) { - childList.add(node.animation); + int size = mNodes.size(); + for (int i = 0; i < size; i++) { + Node node = mNodes.get(i); + childList.add(node.mAnimation); } return childList; } @@ -231,8 +242,10 @@ public final class AnimatorSet extends Animator { */ @Override public void setTarget(Object target) { - for (Node node : mNodes) { - Animator animation = node.animation; + int size = mNodes.size(); + for (int i = 0; i < size; i++) { + Node node = mNodes.get(i); + Animator animation = node.mAnimation; if (animation instanceof AnimatorSet) { ((AnimatorSet)animation).setTarget(target); } else if (animation instanceof ObjectAnimator) { @@ -249,7 +262,7 @@ public final class AnimatorSet extends Animator { int conf = super.getChangingConfigurations(); final int nodeCount = mNodes.size(); for (int i = 0; i < nodeCount; i ++) { - conf |= mNodes.get(i).animation.getChangingConfigurations(); + conf |= mNodes.get(i).mAnimation.getChangingConfigurations(); } return conf; } @@ -303,7 +316,6 @@ public final class AnimatorSet extends Animator { */ public Builder play(Animator anim) { if (anim != null) { - mNeedsSort = true; return new Builder(anim); } return null; @@ -323,22 +335,20 @@ public final class AnimatorSet extends Animator { ArrayList<AnimatorListener> tmpListeners = null; if (mListeners != null) { tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone(); - for (AnimatorListener listener : tmpListeners) { - listener.onAnimationCancel(this); + int size = tmpListeners.size(); + for (int i = 0; i < size; i++) { + tmpListeners.get(i).onAnimationCancel(this); } } - if (mDelayAnim != null && mDelayAnim.isRunning()) { - // If we're currently in the startDelay period, just cancel that animator and - // send out the end event to all listeners - mDelayAnim.cancel(); - } else if (mSortedNodes.size() > 0) { - for (Node node : mSortedNodes) { - node.animation.cancel(); - } + ArrayList<Animator> playingSet = new ArrayList<>(mPlayingSet); + int setSize = playingSet.size(); + for (int i = 0; i < setSize; i++) { + playingSet.get(i).cancel(); } if (tmpListeners != null) { - for (AnimatorListener listener : tmpListeners) { - listener.onAnimationEnd(this); + int size = tmpListeners.size(); + for (int i = 0; i < size; i++) { + tmpListeners.get(i).onAnimationEnd(this); } } mStarted = false; @@ -355,35 +365,45 @@ public final class AnimatorSet extends Animator { public void end() { mTerminated = true; if (isStarted()) { - if (mSortedNodes.size() != mNodes.size()) { - // hasn't been started yet - sort the nodes now, then end them - sortNodes(); - for (Node node : mSortedNodes) { - if (mSetListener == null) { - mSetListener = new AnimatorSetListener(this); - } - node.animation.addListener(mSetListener); - } - } - if (mDelayAnim != null) { - mDelayAnim.cancel(); - } - if (mSortedNodes.size() > 0) { - for (Node node : mSortedNodes) { - node.animation.end(); - } + endRemainingAnimations(); + } + if (mListeners != null) { + ArrayList<AnimatorListener> tmpListeners = + (ArrayList<AnimatorListener>) mListeners.clone(); + for (int i = 0; i < tmpListeners.size(); i++) { + tmpListeners.get(i).onAnimationEnd(this); } - if (mListeners != null) { - ArrayList<AnimatorListener> tmpListeners = - (ArrayList<AnimatorListener>) mListeners.clone(); - for (AnimatorListener listener : tmpListeners) { - listener.onAnimationEnd(this); + } + mStarted = false; + } + + /** + * Iterate the animations that haven't finished or haven't started, and end them. + */ + private void endRemainingAnimations() { + ArrayList<Animator> remainingList = new ArrayList<Animator>(mNodes.size()); + remainingList.addAll(mPlayingSet); + + int index = 0; + while (index < remainingList.size()) { + Animator anim = remainingList.get(index); + anim.end(); + index++; + Node node = mNodeMap.get(anim); + if (node.mChildNodes != null) { + int childSize = node.mChildNodes.size(); + for (int i = 0; i < childSize; i++) { + Node child = node.mChildNodes.get(i); + if (child.mLatestParent != node) { + continue; + } + remainingList.add(child.mAnimation); } } - mStarted = false; } } + /** * Returns true if any of the child animations of this AnimatorSet have been started and have * not yet ended. @@ -391,8 +411,9 @@ public final class AnimatorSet extends Animator { */ @Override public boolean isRunning() { - for (Node node : mNodes) { - if (node.animation.isRunning()) { + int size = mNodes.size(); + for (int i = 0; i < size; i++) { + if (mNodes.get(i).mAnimation.isRunning()) { return true; } } @@ -426,7 +447,24 @@ public final class AnimatorSet extends Animator { if (mStartDelay > 0) { mReversible = false; } + long delta = startDelay - mStartDelay; mStartDelay = startDelay; + if (!mDependencyDirty) { + // Dependency graph already constructed, update all the nodes' start/end time + int size = mNodes.size(); + for (int i = 0; i < size; i++) { + Node node = mNodes.get(i); + if (node == mRootNode) { + node.mEndTime = mStartDelay; + } else { + node.mStartTime = node.mStartTime == DURATION_INFINITE ? + DURATION_INFINITE : node.mStartTime + delta; + node.mEndTime = node.mEndTime == DURATION_INFINITE ? + DURATION_INFINITE : node.mEndTime + delta; + + } + } + } } /** @@ -455,6 +493,7 @@ public final class AnimatorSet extends Animator { if (duration < 0) { throw new IllegalArgumentException("duration must be a value of zero or greater"); } + mDependencyDirty = true; // Just record the value for now - it will be used later when the AnimatorSet starts mDuration = duration; return this; @@ -462,15 +501,19 @@ public final class AnimatorSet extends Animator { @Override public void setupStartValues() { - for (Node node : mNodes) { - node.animation.setupStartValues(); + int size = mNodes.size(); + for (int i = 0; i < size; i++) { + Node node = mNodes.get(i); + node.mAnimation.setupStartValues(); } } @Override public void setupEndValues() { - for (Node node : mNodes) { - node.animation.setupEndValues(); + int size = mNodes.size(); + for (int i = 0; i < size; i++) { + Node node = mNodes.get(i); + node.mAnimation.setupEndValues(); } } @@ -482,8 +525,10 @@ public final class AnimatorSet extends Animator { if (mDelayAnim != null) { mDelayAnim.pause(); } else { - for (Node node : mNodes) { - node.animation.pause(); + int size = mNodes.size(); + for (int i = 0; i < size; i++) { + Node node = mNodes.get(i); + node.mAnimation.pause(); } } } @@ -497,8 +542,10 @@ public final class AnimatorSet extends Animator { if (mDelayAnim != null) { mDelayAnim.resume(); } else { - for (Node node : mNodes) { - node.animation.resume(); + int size = mNodes.size(); + for (int i = 0; i < size; i++) { + Node node = mNodes.get(i); + node.mAnimation.resume(); } } } @@ -518,96 +565,25 @@ public final class AnimatorSet extends Animator { mStarted = true; mPaused = false; - for (Node node : mNodes) { - node.animation.setAllowRunningAsynchronously(false); + int size = mNodes.size(); + for (int i = 0; i < size; i++) { + Node node = mNodes.get(i); + node.mEnded = false; + node.mAnimation.setAllowRunningAsynchronously(false); } - if (mDuration >= 0) { - // If the duration was set on this AnimatorSet, pass it along to all child animations - for (Node node : mNodes) { - // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to - // insert "play-after" delays - node.animation.setDuration(mDuration); - } - } if (mInterpolator != null) { - for (Node node : mNodes) { - node.animation.setInterpolator(mInterpolator); - } - } - // First, sort the nodes (if necessary). This will ensure that sortedNodes - // contains the animation nodes in the correct order. - sortNodes(); - - int numSortedNodes = mSortedNodes.size(); - for (int i = 0; i < numSortedNodes; ++i) { - Node node = mSortedNodes.get(i); - // First, clear out the old listeners - ArrayList<AnimatorListener> oldListeners = node.animation.getListeners(); - if (oldListeners != null && oldListeners.size() > 0) { - final ArrayList<AnimatorListener> clonedListeners = new - ArrayList<AnimatorListener>(oldListeners); - - for (AnimatorListener listener : clonedListeners) { - if (listener instanceof DependencyListener || - listener instanceof AnimatorSetListener) { - node.animation.removeListener(listener); - } - } + for (int i = 0; i < size; i++) { + Node node = mNodes.get(i); + node.mAnimation.setInterpolator(mInterpolator); } } - // nodesToStart holds the list of nodes to be started immediately. We don't want to - // start the animations in the loop directly because we first need to set up - // dependencies on all of the nodes. For example, we don't want to start an animation - // when some other animation also wants to start when the first animation begins. - final ArrayList<Node> nodesToStart = new ArrayList<Node>(); - for (int i = 0; i < numSortedNodes; ++i) { - Node node = mSortedNodes.get(i); - if (mSetListener == null) { - mSetListener = new AnimatorSetListener(this); - } - if (node.dependencies == null || node.dependencies.size() == 0) { - nodesToStart.add(node); - } else { - int numDependencies = node.dependencies.size(); - for (int j = 0; j < numDependencies; ++j) { - Dependency dependency = node.dependencies.get(j); - dependency.node.animation.addListener( - new DependencyListener(this, node, dependency.rule)); - } - node.tmpDependencies = (ArrayList<Dependency>) node.dependencies.clone(); - } - node.animation.addListener(mSetListener); - } + updateAnimatorsDuration(); + createDependencyGraph(); + // Now that all dependencies are set up, start the animations that should be started. - if (mStartDelay <= 0) { - for (Node node : nodesToStart) { - node.animation.start(); - mPlayingSet.add(node.animation); - } - } else { - mDelayAnim = ValueAnimator.ofFloat(0f, 1f); - mDelayAnim.setDuration(mStartDelay); - mDelayAnim.addListener(new AnimatorListenerAdapter() { - boolean canceled = false; - public void onAnimationCancel(Animator anim) { - canceled = true; - } - public void onAnimationEnd(Animator anim) { - if (!canceled) { - int numNodes = nodesToStart.size(); - for (int i = 0; i < numNodes; ++i) { - Node node = nodesToStart.get(i); - node.animation.start(); - mPlayingSet.add(node.animation); - } - } - mDelayAnim = null; - } - }); - mDelayAnim.start(); - } + start(mRootNode); if (mListeners != null) { ArrayList<AnimatorListener> tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone(); @@ -631,6 +607,27 @@ public final class AnimatorSet extends Animator { } } + private void updateAnimatorsDuration() { + if (mDuration >= 0) { + // If the duration was set on this AnimatorSet, pass it along to all child animations + int size = mNodes.size(); + for (int i = 0; i < size; i++) { + Node node = mNodes.get(i); + // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to + // insert "play-after" delays + node.mAnimation.setDuration(mDuration); + } + } + mDelayAnim.setDuration(mStartDelay); + } + + void start(final Node node) { + final Animator anim = node.mAnimation; + mPlayingSet.add(anim); + anim.addListener(mSetListener); + anim.start(); + } + @Override public AnimatorSet clone() { final AnimatorSet anim = (AnimatorSet) super.clone(); @@ -643,15 +640,13 @@ public final class AnimatorSet extends Animator { * and will populate any appropriate lists, when it is started. */ final int nodeCount = mNodes.size(); - anim.mNeedsSort = true; anim.mTerminated = false; anim.mStarted = false; anim.mPlayingSet = new ArrayList<Animator>(); anim.mNodeMap = new ArrayMap<Animator, Node>(); anim.mNodes = new ArrayList<Node>(nodeCount); - anim.mSortedNodes = new ArrayList<Node>(nodeCount); anim.mReversible = mReversible; - anim.mSetListener = null; + anim.mSetListener = new AnimatorSetListener(anim); // Walk through the old nodes list, cloning each node and adding it to the new nodemap. // One problem is that the old node dependencies point to nodes in the old AnimatorSet. @@ -662,16 +657,10 @@ public final class AnimatorSet extends Animator { Node nodeClone = node.clone(); node.mTmpClone = nodeClone; anim.mNodes.add(nodeClone); - anim.mNodeMap.put(nodeClone.animation, nodeClone); - // Clear out the dependencies in the clone; we'll set these up manually later - nodeClone.dependencies = null; - nodeClone.tmpDependencies = null; - nodeClone.nodeDependents = null; - nodeClone.nodeDependencies = null; - - // clear out any listeners that were set up by the AnimatorSet; these will - // be set up when the clone's nodes are sorted - final ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners(); + anim.mNodeMap.put(nodeClone.mAnimation, nodeClone); + + // clear out any listeners that were set up by the AnimatorSet + final ArrayList<AnimatorListener> cloneListeners = nodeClone.mAnimation.getListeners(); if (cloneListeners != null) { for (int i = cloneListeners.size() - 1; i >= 0; i--) { final AnimatorListener listener = cloneListeners.get(i); @@ -681,127 +670,37 @@ public final class AnimatorSet extends Animator { } } } + + anim.mRootNode = mRootNode.mTmpClone; + anim.mDelayAnim = (ValueAnimator) anim.mRootNode.mAnimation; + // Now that we've cloned all of the nodes, we're ready to walk through their // dependencies, mapping the old dependencies to the new nodes - for (int n = 0; n < nodeCount; n++) { - final Node node = mNodes.get(n); - final Node clone = node.mTmpClone; - if (node.dependencies != null) { - clone.dependencies = new ArrayList<Dependency>(node.dependencies.size()); - final int depSize = node.dependencies.size(); - for (int i = 0; i < depSize; i ++) { - final Dependency dependency = node.dependencies.get(i); - Dependency cloneDependency = new Dependency(dependency.node.mTmpClone, - dependency.rule); - clone.dependencies.add(cloneDependency); - } + for (int i = 0; i < nodeCount; i++) { + Node node = mNodes.get(i); + // Update dependencies for node's clone + node.mTmpClone.mLatestParent = node.mLatestParent == null ? + null : node.mLatestParent.mTmpClone; + int size = node.mChildNodes == null ? 0 : node.mChildNodes.size(); + for (int j = 0; j < size; j++) { + node.mTmpClone.mChildNodes.set(j, node.mChildNodes.get(j).mTmpClone); } - if (node.nodeDependents != null) { - clone.nodeDependents = new ArrayList<Node>(node.nodeDependents.size()); - for (Node dep : node.nodeDependents) { - clone.nodeDependents.add(dep.mTmpClone); - } + size = node.mSiblings == null ? 0 : node.mSiblings.size(); + for (int j = 0; j < size; j++) { + node.mTmpClone.mSiblings.set(j, node.mSiblings.get(j).mTmpClone); } - if (node.nodeDependencies != null) { - clone.nodeDependencies = new ArrayList<Node>(node.nodeDependencies.size()); - for (Node dep : node.nodeDependencies) { - clone.nodeDependencies.add(dep.mTmpClone); - } + size = node.mParents == null ? 0 : node.mParents.size(); + for (int j = 0; j < size; j++) { + node.mTmpClone.mParents.set(j, node.mParents.get(j).mTmpClone); } } + for (int n = 0; n < nodeCount; n++) { mNodes.get(n).mTmpClone = null; } return anim; } - /** - * This class is the mechanism by which animations are started based on events in other - * animations. If an animation has multiple dependencies on other animations, then - * all dependencies must be satisfied before the animation is started. - */ - private static class DependencyListener implements AnimatorListener { - - private AnimatorSet mAnimatorSet; - - // The node upon which the dependency is based. - private Node mNode; - - // The Dependency rule (WITH or AFTER) that the listener should wait for on - // the node - private int mRule; - - public DependencyListener(AnimatorSet animatorSet, Node node, int rule) { - this.mAnimatorSet = animatorSet; - this.mNode = node; - this.mRule = rule; - } - - /** - * Ignore cancel events for now. We may want to handle this eventually, - * to prevent follow-on animations from running when some dependency - * animation is canceled. - */ - public void onAnimationCancel(Animator animation) { - } - - /** - * An end event is received - see if this is an event we are listening for - */ - public void onAnimationEnd(Animator animation) { - if (mRule == Dependency.AFTER) { - startIfReady(animation); - } - } - - /** - * Ignore repeat events for now - */ - public void onAnimationRepeat(Animator animation) { - } - - /** - * A start event is received - see if this is an event we are listening for - */ - public void onAnimationStart(Animator animation) { - if (mRule == Dependency.WITH) { - startIfReady(animation); - } - } - - /** - * Check whether the event received is one that the node was waiting for. - * If so, mark it as complete and see whether it's time to start - * the animation. - * @param dependencyAnimation the animation that sent the event. - */ - private void startIfReady(Animator dependencyAnimation) { - if (mAnimatorSet.mTerminated) { - // if the parent AnimatorSet was canceled, then don't start any dependent anims - return; - } - Dependency dependencyToRemove = null; - int numDependencies = mNode.tmpDependencies.size(); - for (int i = 0; i < numDependencies; ++i) { - Dependency dependency = mNode.tmpDependencies.get(i); - if (dependency.rule == mRule && - dependency.node.animation == dependencyAnimation) { - // rule fired - remove the dependency and listener and check to - // see whether it's time to start the animation - dependencyToRemove = dependency; - dependencyAnimation.removeListener(this); - break; - } - } - mNode.tmpDependencies.remove(dependencyToRemove); - if (mNode.tmpDependencies.size() == 0) { - // all dependencies satisfied: start the animation - mNode.animation.start(); - mAnimatorSet.mPlayingSet.add(mNode.animation); - } - } - - } private class AnimatorSetListener implements AnimatorListener { @@ -812,14 +711,16 @@ public final class AnimatorSet extends Animator { } public void onAnimationCancel(Animator animation) { + if (!mTerminated) { // Listeners are already notified of the AnimatorSet canceling in cancel(). // The logic below only kicks in when animations end normally - if (mPlayingSet.size() == 0) { - if (mListeners != null) { - int numListeners = mListeners.size(); + if (mAnimatorSet.mPlayingSet.size() == 0) { + ArrayList<AnimatorListener> listeners = mAnimatorSet.mListeners; + if (listeners != null) { + int numListeners = listeners.size(); for (int i = 0; i < numListeners; ++i) { - mListeners.get(i).onAnimationCancel(mAnimatorSet); + listeners.get(i).onAnimationCancel(mAnimatorSet); } } } @@ -829,17 +730,26 @@ public final class AnimatorSet extends Animator { @SuppressWarnings("unchecked") public void onAnimationEnd(Animator animation) { animation.removeListener(this); - mPlayingSet.remove(animation); + mAnimatorSet.mPlayingSet.remove(animation); Node animNode = mAnimatorSet.mNodeMap.get(animation); - animNode.done = true; + animNode.mEnded = true; + if (!mTerminated) { + List<Node> children = animNode.mChildNodes; + // Start children animations, if any. + int childrenSize = children == null ? 0 : children.size(); + for (int i = 0; i < childrenSize; i++) { + if (children.get(i).mLatestParent == animNode) { + mAnimatorSet.start(children.get(i)); + } + } // Listeners are already notified of the AnimatorSet ending in cancel() or // end(); the logic below only kicks in when animations end normally - ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes; boolean allDone = true; - int numSortedNodes = sortedNodes.size(); - for (int i = 0; i < numSortedNodes; ++i) { - if (!sortedNodes.get(i).done) { + // Traverse the tree and find if there's any unfinished node + int size = mNodes.size(); + for (int i = 0; i < size; i++) { + if (!mNodes.get(i).mEnded) { allDone = false; break; } @@ -872,79 +782,6 @@ public final class AnimatorSet extends Animator { } /** - * This method sorts the current set of nodes, if needed. The sort is a simple - * DependencyGraph sort, which goes like this: - * - All nodes without dependencies become 'roots' - * - while roots list is not null - * - for each root r - * - add r to sorted list - * - remove r as a dependency from any other node - * - any nodes with no dependencies are added to the roots list - */ - private void sortNodes() { - if (mNeedsSort) { - mSortedNodes.clear(); - ArrayList<Node> roots = new ArrayList<Node>(); - int numNodes = mNodes.size(); - for (int i = 0; i < numNodes; ++i) { - Node node = mNodes.get(i); - if (node.dependencies == null || node.dependencies.size() == 0) { - roots.add(node); - } - } - ArrayList<Node> tmpRoots = new ArrayList<Node>(); - while (roots.size() > 0) { - int numRoots = roots.size(); - for (int i = 0; i < numRoots; ++i) { - Node root = roots.get(i); - mSortedNodes.add(root); - if (root.nodeDependents != null) { - int numDependents = root.nodeDependents.size(); - for (int j = 0; j < numDependents; ++j) { - Node node = root.nodeDependents.get(j); - node.nodeDependencies.remove(root); - if (node.nodeDependencies.size() == 0) { - tmpRoots.add(node); - } - } - } - } - roots.clear(); - roots.addAll(tmpRoots); - tmpRoots.clear(); - } - mNeedsSort = false; - if (mSortedNodes.size() != mNodes.size()) { - throw new IllegalStateException("Circular dependencies cannot exist" - + " in AnimatorSet"); - } - } else { - // Doesn't need sorting, but still need to add in the nodeDependencies list - // because these get removed as the event listeners fire and the dependencies - // are satisfied - int numNodes = mNodes.size(); - for (int i = 0; i < numNodes; ++i) { - Node node = mNodes.get(i); - if (node.dependencies != null && node.dependencies.size() > 0) { - int numDependencies = node.dependencies.size(); - for (int j = 0; j < numDependencies; ++j) { - Dependency dependency = node.dependencies.get(j); - if (node.nodeDependencies == null) { - node.nodeDependencies = new ArrayList<Node>(); - } - if (!node.nodeDependencies.contains(dependency.node)) { - node.nodeDependencies.add(dependency.node); - } - } - } - // nodes are 'done' by default; they become un-done when started, and done - // again when ended - node.done = false; - } - } - } - - /** * @hide */ @Override @@ -953,8 +790,10 @@ public final class AnimatorSet extends Animator { return false; } // Loop to make sure all the Nodes can reverse. - for (Node node : mNodes) { - if (!node.animation.canReverse() || node.animation.getStartDelay() > 0) { + int size = mNodes.size(); + for (int i = 0; i < size; i++) { + Node node = mNodes.get(i); + if (!node.mAnimation.canReverse() || node.mAnimation.getStartDelay() > 0) { return false; } } @@ -967,8 +806,10 @@ public final class AnimatorSet extends Animator { @Override public void reverse() { if (canReverse()) { - for (Node node : mNodes) { - node.animation.reverse(); + int size = mNodes.size(); + for (int i = 0; i < size; i++) { + Node node = mNodes.get(i); + node.mAnimation.reverse(); } } } @@ -976,88 +817,244 @@ public final class AnimatorSet extends Animator { @Override public String toString() { String returnVal = "AnimatorSet@" + Integer.toHexString(hashCode()) + "{"; - boolean prevNeedsSort = mNeedsSort; - sortNodes(); - mNeedsSort = prevNeedsSort; - for (Node node : mSortedNodes) { - returnVal += "\n " + node.animation.toString(); + int size = mNodes.size(); + for (int i = 0; i < size; i++) { + Node node = mNodes.get(i); + returnVal += "\n " + node.mAnimation.toString(); } return returnVal + "\n}"; } + private void printChildCount() { + // Print out the child count through a level traverse. + ArrayList<Node> list = new ArrayList<>(mNodes.size()); + list.add(mRootNode); + Log.d(TAG, "Current tree: "); + int index = 0; + while (index < list.size()) { + int listSize = list.size(); + StringBuilder builder = new StringBuilder(); + for (; index < listSize; index++) { + Node node = list.get(index); + int num = 0; + if (node.mChildNodes != null) { + for (int i = 0; i < node.mChildNodes.size(); i++) { + Node child = node.mChildNodes.get(i); + if (child.mLatestParent == node) { + num++; + list.add(child); + } + } + } + builder.append(" "); + builder.append(num); + } + Log.d(TAG, builder.toString()); + } + } + + private void createDependencyGraph() { + if (!mDependencyDirty) { + return; + } + + // TODO: In addition to checking the dirty flag, we should also cache the duration for + // each animator, so that when the animator's duration is changed, we can detect that and + // update the dependency graph. + + mDependencyDirty = false; + // Traverse all the siblings and make sure they have all the parents + int size = mNodes.size(); + for (int i = 0; i < size; i++) { + mNodes.get(i).mParentsAdded = false; + } + for (int i = 0; i < size; i++) { + Node node = mNodes.get(i); + if (node.mParentsAdded) { + continue; + } + + node.mParentsAdded = true; + if (node.mSiblings == null) { + continue; + } + + // Find all the siblings + findSiblings(node, node.mSiblings); + node.mSiblings.remove(node); + + // Get parents from all siblings + int siblingSize = node.mSiblings.size(); + for (int j = 0; j < siblingSize; j++) { + node.addParents(node.mSiblings.get(j).mParents); + } + + // Now make sure all siblings share the same set of parents + for (int j = 0; j < siblingSize; j++) { + Node sibling = node.mSiblings.get(j); + sibling.addParents(node.mParents); + sibling.mParentsAdded = true; + } + } + + for (int i = 0; i < size; i++) { + Node node = mNodes.get(i); + if (node != mRootNode && node.mParents == null) { + node.addParent(mRootNode); + } + } + + // Do a DFS on the tree + ArrayList<Node> visited = new ArrayList<Node>(mNodes.size()); + // Assign start/end time + mRootNode.mStartTime = 0; + mRootNode.mEndTime = mDelayAnim.getDuration(); + updatePlayTime(mRootNode, visited); + + long maxEndTime = 0; + for (int i = 0; i < size; i++) { + Node node = mNodes.get(i); + if (node.mEndTime == DURATION_INFINITE) { + maxEndTime = DURATION_INFINITE; + break; + } else { + maxEndTime = node.mEndTime > maxEndTime ? node.mEndTime : maxEndTime; + } + } + mTotalDuration = maxEndTime; + } + /** - * Dependency holds information about the node that some other node is - * dependent upon and the nature of that dependency. - * + * Based on parent's start/end time, calculate children's start/end time. If cycle exists in + * the graph, all the nodes on the cycle will be marked to start at {@link #DURATION_INFINITE}, + * meaning they will ever play. */ - private static class Dependency { - static final int WITH = 0; // dependent node must start with this dependency node - static final int AFTER = 1; // dependent node must start when this dependency node finishes + private void updatePlayTime(Node parent, ArrayList<Node> visited) { + if (parent.mChildNodes == null) { + return; + } + + visited.add(parent); + int childrenSize = parent.mChildNodes.size(); + for (int i = 0; i < childrenSize; i++) { + Node child = parent.mChildNodes.get(i); + int index = visited.indexOf(child); + if (index >= 0) { + // Child has been visited, cycle found. Mark all the nodes in the cycle. + for (int j = index; j < visited.size(); i++) { + visited.get(j).mLatestParent = null; + visited.get(j).mStartTime = DURATION_INFINITE; + visited.get(j).mEndTime = DURATION_INFINITE; + } + child.mStartTime = DURATION_INFINITE; + child.mEndTime = DURATION_INFINITE; + child.mLatestParent = null; + Log.w(TAG, "Cycle found in AnimatorSet: " + this); + continue; + } - // The node that the other node with this Dependency is dependent upon - public Node node; + if (child.mStartTime != DURATION_INFINITE) { + if (parent.mEndTime == DURATION_INFINITE) { + child.mLatestParent = parent; + child.mStartTime = DURATION_INFINITE; + child.mEndTime = DURATION_INFINITE; + } else { + if (parent.mEndTime >= child.mStartTime) { + child.mLatestParent = parent; + child.mStartTime = parent.mEndTime; + } - // The nature of the dependency (WITH or AFTER) - public int rule; + long duration = child.mAnimation.getTotalDuration(); + child.mEndTime = duration == DURATION_INFINITE ? + DURATION_INFINITE : child.mStartTime + duration; + } + } + updatePlayTime(child, visited); + } + visited.remove(parent); + } - public Dependency(Node node, int rule) { - this.node = node; - this.rule = rule; + // Recursively find all the siblings + private void findSiblings(Node node, ArrayList<Node> siblings) { + if (!siblings.contains(node)) { + siblings.add(node); + if (node.mSiblings == null) { + return; + } + for (int i = 0; i < node.mSiblings.size(); i++) { + findSiblings(node.mSiblings.get(i), siblings); + } } } /** + * @hide + */ + @Override + public long getTotalDuration() { + updateAnimatorsDuration(); + createDependencyGraph(); + return mTotalDuration; + } + + private Node getNodeForAnimation(Animator anim) { + Node node = mNodeMap.get(anim); + if (node == null) { + node = new Node(anim); + mNodeMap.put(anim, node); + mNodes.add(node); + } + return node; + } + + /** * A Node is an embodiment of both the Animator that it wraps as well as * any dependencies that are associated with that Animation. This includes * both dependencies upon other nodes (in the dependencies list) as * well as dependencies of other nodes upon this (in the nodeDependents list). */ private static class Node implements Cloneable { - public Animator animation; + Animator mAnimation; /** - * These are the dependencies that this node's animation has on other - * nodes. For example, if this node's animation should begin with some - * other animation ends, then there will be an item in this node's - * dependencies list for that other animation's node. + * Child nodes are the nodes associated with animations that will be played immediately + * after current node. */ - public ArrayList<Dependency> dependencies = null; + ArrayList<Node> mChildNodes = null; /** - * tmpDependencies is a runtime detail. We use the dependencies list for sorting. - * But we also use the list to keep track of when multiple dependencies are satisfied, - * but removing each dependency as it is satisfied. We do not want to remove - * the dependency itself from the list, because we need to retain that information - * if the AnimatorSet is launched in the future. So we create a copy of the dependency - * list when the AnimatorSet starts and use this tmpDependencies list to track the - * list of satisfied dependencies. + * Temporary field to hold the clone in AnimatorSet#clone. Cleaned after clone is complete */ - public ArrayList<Dependency> tmpDependencies = null; + private Node mTmpClone = null; /** - * nodeDependencies is just a list of the nodes that this Node is dependent upon. - * This information is used in sortNodes(), to determine when a node is a root. + * Flag indicating whether the animation in this node is finished. This flag + * is used by AnimatorSet to check, as each animation ends, whether all child animations + * are mEnded and it's time to send out an end event for the entire AnimatorSet. */ - public ArrayList<Node> nodeDependencies = null; + boolean mEnded = false; /** - * nodeDepdendents is the list of nodes that have this node as a dependency. This - * is a utility field used in sortNodes to facilitate removing this node as a - * dependency when it is a root node. + * Nodes with animations that are defined to play simultaneously with the animation + * associated with this current node. */ - public ArrayList<Node> nodeDependents = null; + ArrayList<Node> mSiblings; /** - * Flag indicating whether the animation in this node is finished. This flag - * is used by AnimatorSet to check, as each animation ends, whether all child animations - * are done and it's time to send out an end event for the entire AnimatorSet. + * Parent nodes are the nodes with animations preceding current node's animation. Parent + * nodes here are derived from user defined animation sequence. */ - public boolean done = false; + ArrayList<Node> mParents; /** - * Temporary field to hold the clone in AnimatorSet#clone. Cleaned after clone is complete + * Latest parent is the parent node associated with a animation that finishes after all + * the other parents' animations. */ - private Node mTmpClone = null; + Node mLatestParent = null; + + boolean mParentsAdded = false; + long mStartTime = 0; + long mEndTime = 0; /** * Constructs the Node with the animation that it encapsulates. A Node has no @@ -1067,41 +1064,69 @@ public final class AnimatorSet extends Animator { * @param animation The animation that the Node encapsulates. */ public Node(Animator animation) { - this.animation = animation; - } - - /** - * Add a dependency to this Node. The dependency includes information about the - * node that this node is dependency upon and the nature of the dependency. - * @param dependency - */ - public void addDependency(Dependency dependency) { - if (dependencies == null) { - dependencies = new ArrayList<Dependency>(); - nodeDependencies = new ArrayList<Node>(); - } - dependencies.add(dependency); - if (!nodeDependencies.contains(dependency.node)) { - nodeDependencies.add(dependency.node); - } - Node dependencyNode = dependency.node; - if (dependencyNode.nodeDependents == null) { - dependencyNode.nodeDependents = new ArrayList<Node>(); - } - dependencyNode.nodeDependents.add(this); + this.mAnimation = animation; } @Override public Node clone() { try { Node node = (Node) super.clone(); - node.animation = animation.clone(); - node.done = false; + node.mAnimation = mAnimation.clone(); + if (mChildNodes != null) { + node.mChildNodes = new ArrayList<>(mChildNodes); + } + if (mSiblings != null) { + node.mSiblings = new ArrayList<>(mSiblings); + } + if (mParents != null) { + node.mParents = new ArrayList<>(mParents); + } + node.mEnded = false; return node; } catch (CloneNotSupportedException e) { throw new AssertionError(); } } + + void addChild(Node node) { + if (mChildNodes == null) { + mChildNodes = new ArrayList<>(); + } + if (!mChildNodes.contains(node)) { + mChildNodes.add(node); + node.addParent(this); + } + } + + public void addSibling(Node node) { + if (mSiblings == null) { + mSiblings = new ArrayList<Node>(); + } + if (!mSiblings.contains(node)) { + mSiblings.add(node); + node.addSibling(this); + } + } + + public void addParent(Node node) { + if (mParents == null) { + mParents = new ArrayList<Node>(); + } + if (!mParents.contains(node)) { + mParents.add(node); + node.addChild(this); + } + } + + public void addParents(ArrayList<Node> parents) { + if (parents == null) { + return; + } + int size = parents.size(); + for (int i = 0; i < size; i++) { + addParent(parents.get(i)); + } + } } /** @@ -1172,12 +1197,8 @@ public final class AnimatorSet extends Animator { * the other methods of this Builder object. */ Builder(Animator anim) { - mCurrentNode = mNodeMap.get(anim); - if (mCurrentNode == null) { - mCurrentNode = new Node(anim); - mNodeMap.put(anim, mCurrentNode); - mNodes.add(mCurrentNode); - } + mDependencyDirty = true; + mCurrentNode = getNodeForAnimation(anim); } /** @@ -1188,14 +1209,8 @@ public final class AnimatorSet extends Animator { * {@link AnimatorSet#play(Animator)} method starts. */ public Builder with(Animator anim) { - Node node = mNodeMap.get(anim); - if (node == null) { - node = new Node(anim); - mNodeMap.put(anim, node); - mNodes.add(node); - } - Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH); - node.addDependency(dependency); + Node node = getNodeForAnimation(anim); + mCurrentNode.addSibling(node); return this; } @@ -1209,14 +1224,8 @@ public final class AnimatorSet extends Animator { */ public Builder before(Animator anim) { mReversible = false; - Node node = mNodeMap.get(anim); - if (node == null) { - node = new Node(anim); - mNodeMap.put(anim, node); - mNodes.add(node); - } - Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER); - node.addDependency(dependency); + Node node = getNodeForAnimation(anim); + mCurrentNode.addChild(node); return this; } @@ -1230,14 +1239,8 @@ public final class AnimatorSet extends Animator { */ public Builder after(Animator anim) { mReversible = false; - Node node = mNodeMap.get(anim); - if (node == null) { - node = new Node(anim); - mNodeMap.put(anim, node); - mNodes.add(node); - } - Dependency dependency = new Dependency(node, Dependency.AFTER); - mCurrentNode.addDependency(dependency); + Node node = getNodeForAnimation(anim); + mCurrentNode.addParent(node); return this; } diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index a455f8b3a8e4..35a8816bd703 100644 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -575,6 +575,18 @@ public class ValueAnimator extends Animator { } /** + * @hide + */ + @Override + public long getTotalDuration() { + if (mRepeatCount == INFINITE) { + return DURATION_INFINITE; + } else { + return mUnscaledStartDelay + (mUnscaledDuration * (mRepeatCount + 1)); + } + } + + /** * Sets the position of the animation to the specified point in time. This time should * be between 0 and the total duration of the animation, including any repetition. If * the animation has not yet been started, then it will not advance forward after it is diff --git a/core/java/android/annotation/IntRange.java b/core/java/android/annotation/IntRange.java index 1e3c072267d7..b489c01dfd69 100644 --- a/core/java/android/annotation/IntRange.java +++ b/core/java/android/annotation/IntRange.java @@ -18,6 +18,7 @@ package android.annotation; import java.lang.annotation.Retention; import java.lang.annotation.Target; +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.LOCAL_VARIABLE; import static java.lang.annotation.ElementType.METHOD; @@ -38,7 +39,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; * @hide */ @Retention(SOURCE) -@Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE}) +@Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE,ANNOTATION_TYPE}) public @interface IntRange { /** Smallest value, inclusive */ long from() default Long.MIN_VALUE; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index aaaa745bc2da..7b73d4fc8116 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2199,7 +2199,9 @@ public class Intent implements Parcelable, Cloneable { /** * Activity Action: Start this activity to request system shutdown. * The optional boolean extra field {@link #EXTRA_KEY_CONFIRM} can be set to true - * to request confirmation from the user before shutting down. + * to request confirmation from the user before shutting down. The optional boolean + * extra field {@link #EXTRA_USER_REQUESTED_SHUTDOWN} can be set to true to + * indicate that the shutdown is requested by the user. * * <p class="note">This is a protected intent that can only be sent * by the system. @@ -3513,6 +3515,15 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_KEY_CONFIRM = "android.intent.extra.KEY_CONFIRM"; /** + * Set to true in {@link #ACTION_REQUEST_SHUTDOWN} to indicate that the shutdown is + * requested by the user. + * + * {@hide} + */ + public static final String EXTRA_USER_REQUESTED_SHUTDOWN = + "android.intent.extra.USER_REQUESTED_SHUTDOWN"; + + /** * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED} or * {@link android.content.Intent#ACTION_PACKAGE_CHANGED} intents to override the default action * of restarting the application. diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index af1367cd2dc2..c580083b94d9 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -173,6 +173,28 @@ public abstract class CameraMetadata<TKey> { } } + ArrayList<TKey> vendorKeys = CameraMetadataNative.getAllVendorKeys(keyClass); + + if (vendorKeys != null) { + for (TKey k : vendorKeys) { + String keyName; + if (k instanceof CaptureRequest.Key<?>) { + keyName = ((CaptureRequest.Key<?>) k).getName(); + } else if (k instanceof CaptureResult.Key<?>) { + keyName = ((CaptureResult.Key<?>) k).getName(); + } else if (k instanceof CameraCharacteristics.Key<?>) { + keyName = ((CameraCharacteristics.Key<?>) k).getName(); + } else { + continue; + } + + if (filterTags == null || Arrays.binarySearch(filterTags, + CameraMetadataNative.getTag(keyName)) >= 0) { + keyList.add(k); + } + } + } + return keyList; } diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 7e50fd9f623a..12a2910b2055 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -1078,6 +1078,7 @@ public class CameraMetadataNative implements Parcelable { private native synchronized void nativeWriteValues(int tag, byte[] src); private native synchronized void nativeDump() throws IOException; // dump to ALOGD + private static native ArrayList nativeGetAllVendorKeys(Class keyClass); private static native int nativeGetTagFromKey(String keyName) throws IllegalArgumentException; private static native int nativeGetTypeFromTag(int tag) @@ -1113,6 +1114,19 @@ public class CameraMetadataNative implements Parcelable { return nativeIsEmpty(); } + + /** + * Return a list containing keys of the given key class for all defined vendor tags. + * + * @hide + */ + public static <K> ArrayList<K> getAllVendorKeys(Class<K> keyClass) { + if (keyClass == null) { + throw new NullPointerException(); + } + return (ArrayList<K>) nativeGetAllVendorKeys(keyClass); + } + /** * Convert a key string into the equivalent native tag. * diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 0f37ac720bc7..c4f62baab4a1 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -46,7 +46,7 @@ interface IPowerManager boolean isDeviceIdleMode(); void reboot(boolean confirm, String reason, boolean wait); - void shutdown(boolean confirm, boolean wait); + void shutdown(boolean confirm, String reason, boolean wait); void crash(String message); void setStayOnSetting(int val); diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 9a1a03eb3294..69974fa9d965 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -387,7 +387,13 @@ public final class PowerManager { * @hide */ public static final String REBOOT_RECOVERY = "recovery"; - + + /** + * The value to pass as the 'reason' argument to android_reboot(). + * @hide + */ + public static final String SHUTDOWN_USER_REQUESTED = "userrequested"; + final Context mContext; final IPowerManager mService; final Handler mHandler; @@ -926,13 +932,14 @@ public final class PowerManager { * Turn off the device. * * @param confirm If true, shows a shutdown confirmation dialog. + * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null. * @param wait If true, this call waits for the shutdown to complete and does not return. * * @hide */ - public void shutdown(boolean confirm, boolean wait) { + public void shutdown(boolean confirm, String reason, boolean wait) { try { - mService.shutdown(confirm, wait); + mService.shutdown(confirm, reason, wait); } catch (RemoteException e) { } } diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index 31b58490ba24..4da88ee9da9c 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -75,6 +75,8 @@ public final class Trace { public static final long TRACE_TAG_BIONIC = 1L << 16; /** @hide */ public static final long TRACE_TAG_POWER = 1L << 17; + /** @hide */ + public static final long TRACE_TAG_PACKAGE_MANAGER = 1L << 18; private static final long TRACE_TAG_NOT_READY = 1L << 63; private static final int MAX_SECTION_NAME_LEN = 127; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 862207cc1912..f2bf6dc8ede0 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8346,9 +8346,9 @@ public final class Settings { return true; case AppOpsManager.MODE_DEFAULT: // this is the default operating mode after an app's installation - if (!throwException) { - return context.checkCallingOrSelfPermission(permissionName) == - PackageManager.PERMISSION_GRANTED; + if(context.checkCallingOrSelfPermission(permissionName) == PackageManager + .PERMISSION_GRANTED) { + return true; } default: // this is for all other cases trickled down here... diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index 379796d58dc9..2a3756d6b7b3 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -336,6 +336,14 @@ public class RenderNodeAnimator extends Animator { return mUnscaledDuration; } + /** + * @hide + */ + @Override + public long getTotalDuration() { + return mUnscaledDuration + mUnscaledStartDelay; + } + @Override public boolean isRunning() { return mState == STATE_DELAYED || mState == STATE_RUNNING; diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 6af2e8bf5765..d9faece9485f 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -517,6 +517,7 @@ public class ChooserActivity extends ResolverActivity { private final ResolveInfo mBackupResolveInfo; private final ChooserTarget mChooserTarget; private Drawable mBadgeIcon = null; + private CharSequence mBadgeContentDescription; private Drawable mDisplayIcon; private final Intent mFillInIntent; private final int mFillInFlags; @@ -532,7 +533,9 @@ public class ChooserActivity extends ResolverActivity { if (ri != null) { final ActivityInfo ai = ri.activityInfo; if (ai != null && ai.applicationInfo != null) { - mBadgeIcon = getPackageManager().getApplicationIcon(ai.applicationInfo); + final PackageManager pm = getPackageManager(); + mBadgeIcon = pm.getApplicationIcon(ai.applicationInfo); + mBadgeContentDescription = pm.getApplicationLabel(ai.applicationInfo); } } } @@ -555,6 +558,7 @@ public class ChooserActivity extends ResolverActivity { mBackupResolveInfo = other.mBackupResolveInfo; mChooserTarget = other.mChooserTarget; mBadgeIcon = other.mBadgeIcon; + mBadgeContentDescription = other.mBadgeContentDescription; mDisplayIcon = other.mDisplayIcon; mFillInIntent = fillInIntent; mFillInFlags = flags; @@ -647,6 +651,11 @@ public class ChooserActivity extends ResolverActivity { } @Override + public CharSequence getBadgeContentDescription() { + return mBadgeContentDescription; + } + + @Override public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { return new ChooserTargetInfo(this, fillInIntent, flags); } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 89599e089f75..7dd3bed079fc 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -932,6 +932,11 @@ public class ResolverActivity extends Activity { } @Override + public CharSequence getBadgeContentDescription() { + return null; + } + + @Override public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { return new DisplayResolveInfo(this, fillInIntent, flags); } @@ -1072,6 +1077,11 @@ public class ResolverActivity extends Activity { public Drawable getBadgeIcon(); /** + * @return The content description for the badge icon + */ + public CharSequence getBadgeContentDescription(); + + /** * Clone this target with the given fill-in information. */ public TargetInfo cloneFilledIn(Intent fillInIntent, int flags); @@ -1542,6 +1552,7 @@ public class ResolverActivity extends Activity { final Drawable badge = info.getBadgeIcon(); if (badge != null) { holder.badge.setImageDrawable(badge); + holder.badge.setContentDescription(info.getBadgeContentDescription()); holder.badge.setVisibility(View.VISIBLE); } else { holder.badge.setVisibility(View.GONE); diff --git a/core/java/com/android/internal/app/ShutdownActivity.java b/core/java/com/android/internal/app/ShutdownActivity.java index 97521cf68dd4..745d28f367a3 100644 --- a/core/java/com/android/internal/app/ShutdownActivity.java +++ b/core/java/com/android/internal/app/ShutdownActivity.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.IPowerManager; +import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Slog; @@ -30,6 +31,7 @@ public class ShutdownActivity extends Activity { private static final String TAG = "ShutdownActivity"; private boolean mReboot; private boolean mConfirm; + private boolean mUserRequested; @Override protected void onCreate(Bundle savedInstanceState) { @@ -38,6 +40,7 @@ public class ShutdownActivity extends Activity { Intent intent = getIntent(); mReboot = Intent.ACTION_REBOOT.equals(intent.getAction()); mConfirm = intent.getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false); + mUserRequested = intent.getBooleanExtra(Intent.EXTRA_USER_REQUESTED_SHUTDOWN, false); Slog.i(TAG, "onCreate(): confirm=" + mConfirm); Thread thr = new Thread("ShutdownActivity") { @@ -49,7 +52,9 @@ public class ShutdownActivity extends Activity { if (mReboot) { pm.reboot(mConfirm, null, false); } else { - pm.shutdown(mConfirm, false); + pm.shutdown(mConfirm, + mUserRequested ? PowerManager.SHUTDOWN_USER_REQUESTED : null, + false); } } catch (RemoteException e) { } diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index fc9a1a50801e..76796243f3f5 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -34,6 +34,7 @@ import android.view.ViewParent; import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.animation.AnimationUtils; import android.widget.AbsListView; import android.widget.OverScroller; @@ -609,19 +610,37 @@ public class ResolverDrawerLayout extends ViewGroup { } @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); + public CharSequence getAccessibilityClassName() { + // Since we support scrolling, make this ViewGroup look like a + // ScrollView. This is kind of a hack until we have support for + // specifying auto-scroll behavior. + return android.widget.ScrollView.class.getName(); + } + + @Override + public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfoInternal(info); + if (isEnabled()) { if (mCollapseOffset != 0) { info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); info.setScrollable(true); } } + + // This view should never get accessibility focus, but it's interactive + // via nested scrolling, so we can't hide it completely. + info.removeAction(AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS); } @Override - public boolean performAccessibilityAction(int action, Bundle arguments) { - if (super.performAccessibilityAction(action, arguments)) { + public boolean performAccessibilityActionInternal(int action, Bundle arguments) { + if (action == AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS.getId()) { + // This view should never get accessibility focus. + return false; + } + + if (super.performAccessibilityActionInternal(action, arguments)) { return true; } @@ -629,6 +648,7 @@ public class ResolverDrawerLayout extends ViewGroup { smoothScrollTo(0, 0); return true; } + return false; } diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp index 7c8769d0e93f..fb2268911be3 100644 --- a/core/jni/android_hardware_camera2_CameraMetadata.cpp +++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp @@ -23,7 +23,9 @@ #include <utils/Vector.h> #include <utils/SortedVector.h> #include <utils/KeyedVector.h> +#include <stdio.h> #include <string.h> +#include <vector> #include "jni.h" #include "JNIHelp.h" @@ -45,9 +47,30 @@ static const bool kIsDebug = false; // fully-qualified class name #define CAMERA_METADATA_CLASS_NAME "android/hardware/camera2/impl/CameraMetadataNative" +#define CHARACTERISTICS_KEY_CLASS_NAME "android/hardware/camera2/CameraCharacteristics$Key" +#define REQUEST_KEY_CLASS_NAME "android/hardware/camera2/CaptureRequest$Key" +#define RESULT_KEY_CLASS_NAME "android/hardware/camera2/CaptureResult$Key" using namespace android; +static struct metadata_java_key_offsets_t { + jclass mCharacteristicsKey; + jclass mResultKey; + jclass mRequestKey; + jmethodID mCharacteristicsConstr; + jmethodID mResultConstr; + jmethodID mRequestConstr; + jclass mByteArray; + jclass mInt32Array; + jclass mFloatArray; + jclass mInt64Array; + jclass mDoubleArray; + jclass mRationalArray; + jclass mArrayList; + jmethodID mArrayListConstr; + jmethodID mArrayListAdd; +} gMetadataOffsets; + struct fields_t { jfieldID metadata_ptr; }; @@ -141,6 +164,7 @@ struct Helpers { extern "C" { static void CameraMetadata_classInit(JNIEnv *env, jobject thiz); +static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType); static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName); static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag); static jint CameraMetadata_setupGlobalVendorTagDescriptor(JNIEnv *env, jobject thiz); @@ -510,6 +534,9 @@ static JNINativeMethod gCameraMetadataMethods[] = { { "nativeClassInit", "()V", (void *)CameraMetadata_classInit }, + { "nativeGetAllVendorKeys", + "(Ljava/lang/Class;)Ljava/util/ArrayList;", + (void *)CameraMetadata_getAllVendorKeys}, { "nativeGetTagFromKey", "(Ljava/lang/String;)I", (void *)CameraMetadata_getTagFromKey }, @@ -588,6 +615,44 @@ static int find_fields(JNIEnv *env, field *fields, int count) // Get all the required offsets in java class and register native functions int register_android_hardware_camera2_CameraMetadata(JNIEnv *env) { + + // Store global references to Key-related classes and methods used natively + jclass characteristicsKeyClazz = FindClassOrDie(env, CHARACTERISTICS_KEY_CLASS_NAME); + jclass requestKeyClazz = FindClassOrDie(env, REQUEST_KEY_CLASS_NAME); + jclass resultKeyClazz = FindClassOrDie(env, RESULT_KEY_CLASS_NAME); + gMetadataOffsets.mCharacteristicsKey = MakeGlobalRefOrDie(env, characteristicsKeyClazz); + gMetadataOffsets.mRequestKey = MakeGlobalRefOrDie(env, requestKeyClazz); + gMetadataOffsets.mResultKey = MakeGlobalRefOrDie(env, resultKeyClazz); + gMetadataOffsets.mCharacteristicsConstr = GetMethodIDOrDie(env, + gMetadataOffsets.mCharacteristicsKey, "<init>", + "(Ljava/lang/String;Ljava/lang/Class;)V"); + gMetadataOffsets.mRequestConstr = GetMethodIDOrDie(env, + gMetadataOffsets.mRequestKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;)V"); + gMetadataOffsets.mResultConstr = GetMethodIDOrDie(env, + gMetadataOffsets.mResultKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;)V"); + + // Store global references for primitive array types used by Keys + jclass byteClazz = FindClassOrDie(env, "[B"); + jclass int32Clazz = FindClassOrDie(env, "[I"); + jclass floatClazz = FindClassOrDie(env, "[F"); + jclass int64Clazz = FindClassOrDie(env, "[J"); + jclass doubleClazz = FindClassOrDie(env, "[D"); + jclass rationalClazz = FindClassOrDie(env, "[Landroid/util/Rational;"); + gMetadataOffsets.mByteArray = MakeGlobalRefOrDie(env, byteClazz); + gMetadataOffsets.mInt32Array = MakeGlobalRefOrDie(env, int32Clazz); + gMetadataOffsets.mFloatArray = MakeGlobalRefOrDie(env, floatClazz); + gMetadataOffsets.mInt64Array = MakeGlobalRefOrDie(env, int64Clazz); + gMetadataOffsets.mDoubleArray = MakeGlobalRefOrDie(env, doubleClazz); + gMetadataOffsets.mRationalArray = MakeGlobalRefOrDie(env, rationalClazz); + + // Store global references for ArrayList methods used + jclass arrayListClazz = FindClassOrDie(env, "java/util/ArrayList"); + gMetadataOffsets.mArrayList = MakeGlobalRefOrDie(env, arrayListClazz); + gMetadataOffsets.mArrayListConstr = GetMethodIDOrDie(env, gMetadataOffsets.mArrayList, + "<init>", "(I)V"); + gMetadataOffsets.mArrayListAdd = GetMethodIDOrDie(env, gMetadataOffsets.mArrayList, + "add", "(Ljava/lang/Object;)Z"); + // Register native functions return RegisterMethodsOrDie(env, CAMERA_METADATA_CLASS_NAME, @@ -596,6 +661,7 @@ int register_android_hardware_camera2_CameraMetadata(JNIEnv *env) } extern "C" { + static void CameraMetadata_classInit(JNIEnv *env, jobject thiz) { // XX: Why do this separately instead of doing it in the register function? ALOGV("%s", __FUNCTION__); @@ -612,6 +678,107 @@ static void CameraMetadata_classInit(JNIEnv *env, jobject thiz) { env->FindClass(CAMERA_METADATA_CLASS_NAME); } +static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType) { + + // Get all vendor tags + sp<VendorTagDescriptor> vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor(); + if (vTags.get() == nullptr) { + // No vendor tags. + return NULL; + } + + int count = vTags->getTagCount(); + if (count <= 0) { + // No vendor tags. + return NULL; + } + + std::vector<uint32_t> tagIds(count, /*initializer value*/0); + vTags->getTagArray(&tagIds[0]); + + // Which key class/constructor should we use? + jclass keyClazz; + jmethodID keyConstr; + if (env->IsSameObject(keyType, gMetadataOffsets.mCharacteristicsKey)) { + keyClazz = gMetadataOffsets.mCharacteristicsKey; + keyConstr = gMetadataOffsets.mCharacteristicsConstr; + } else if (env->IsSameObject(keyType, gMetadataOffsets.mResultKey)) { + keyClazz = gMetadataOffsets.mResultKey; + keyConstr = gMetadataOffsets.mResultConstr; + } else if (env->IsSameObject(keyType, gMetadataOffsets.mRequestKey)) { + keyClazz = gMetadataOffsets.mRequestKey; + keyConstr = gMetadataOffsets.mRequestConstr; + } else { + jniThrowException(env, "java/lang/IllegalArgumentException", + "Invalid key class given as argument."); + return NULL; + } + + // Allocate arrayList to return + jobject arrayList = env->NewObject(gMetadataOffsets.mArrayList, + gMetadataOffsets.mArrayListConstr, static_cast<jint>(count)); + if (env->ExceptionCheck()) { + return NULL; + } + + for (uint32_t id : tagIds) { + const char* section = vTags->getSectionName(id); + const char* tag = vTags->getTagName(id); + int type = vTags->getTagType(id); + + size_t totalLen = strlen(section) + strlen(tag) + 2; + std::vector<char> fullName(totalLen, 0); + snprintf(&fullName[0], totalLen, "%s.%s", section, tag); + + jstring name = env->NewStringUTF(&fullName[0]); + + if (env->ExceptionCheck()) { + return NULL; + } + + jclass valueClazz; + switch (type) { + case TYPE_BYTE: + valueClazz = gMetadataOffsets.mByteArray; + break; + case TYPE_INT32: + valueClazz = gMetadataOffsets.mInt32Array; + break; + case TYPE_FLOAT: + valueClazz = gMetadataOffsets.mFloatArray; + break; + case TYPE_INT64: + valueClazz = gMetadataOffsets.mInt64Array; + break; + case TYPE_DOUBLE: + valueClazz = gMetadataOffsets.mDoubleArray; + break; + case TYPE_RATIONAL: + valueClazz = gMetadataOffsets.mRationalArray; + break; + default: + jniThrowExceptionFmt(env, "java/lang/IllegalStateException", + "Invalid type %d given for key %s", type, &fullName[0]); + return NULL; + } + + jobject key = env->NewObject(keyClazz, keyConstr, name, valueClazz); + if (env->ExceptionCheck()) { + return NULL; + } + + env->CallBooleanMethod(arrayList, gMetadataOffsets.mArrayListAdd, key); + if (env->ExceptionCheck()) { + return NULL; + } + + env->DeleteLocalRef(name); + env->DeleteLocalRef(key); + } + + return arrayList; +} + static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName) { ScopedUtfChars keyScoped(env, keyName); diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 1b0793362426..5d63d35e1859 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -713,7 +713,7 @@ <string name="lockscreen_access_pattern_cell_added" msgid="6756031208359292487">"سلول اضافه شد"</string> <string name="lockscreen_access_pattern_cell_added_verbose" msgid="7264580781744026939">"سلول <xliff:g id="CELL_INDEX">%1$s</xliff:g> اضافه شد"</string> <string name="lockscreen_access_pattern_detected" msgid="4988730895554057058">"الگو تکمیل شد"</string> - <string name="lockscreen_access_pattern_area" msgid="400813207572953209">"ناحیه الگو."</string> + <string name="lockscreen_access_pattern_area" msgid="400813207572953209">"ناحیه الگو"</string> <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. ابزارک %2$d از %3$d."</string> <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"ابزارک اضافه کنید."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"خالی"</string> @@ -866,7 +866,7 @@ <string name="deleteText" msgid="6979668428458199034">"حذف"</string> <string name="inputMethod" msgid="1653630062304567879">"روش ورودی"</string> <string name="editTextMenuTitle" msgid="4909135564941815494">"عملکردهای متنی"</string> - <string name="low_internal_storage_view_title" msgid="5576272496365684834">"فضای ذخیرهسازی رو به اتمام است"</string> + <string name="low_internal_storage_view_title" msgid="5576272496365684834">"حافظه درحال پر شدن است"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"برخی از عملکردهای سیستم ممکن است کار نکنند"</string> <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"فضای ذخیرهسازی سیستم کافی نیست. اطمینان حاصل کنید که دارای ۲۵۰ مگابایت فضای خالی هستید و سیستم را راهاندازی مجدد کنید."</string> <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> در حال اجرا است"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 69e390ba50c2..e2c94e440a80 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -898,7 +898,7 @@ <string name="aerr_title" msgid="1905800560317137752"></string> <string name="aerr_application" msgid="932628488013092776">"\"<xliff:g id="APPLICATION">%1$s</xliff:g>\" s\'est arrêté."</string> <string name="aerr_process" msgid="4507058997035697579">"Le processus <xliff:g id="PROCESS">%1$s</xliff:g> s\'est interrompu."</string> - <string name="aerr_process_silence" msgid="4226685530196000222">"Plantage de Silence lors du processus \"<xliff:g id="PROCESS">%1$s</xliff:g>\" avant le redémarrage"</string> + <string name="aerr_process_silence" msgid="4226685530196000222">"Suspendre l\'affichage des informations de plantage de <xliff:g id="PROCESS">%1$s</xliff:g> jusqu\'au redémarrage"</string> <string name="anr_title" msgid="4351948481459135709"></string> <string name="anr_activity_application" msgid="1904477189057199066">"L\'application <xliff:g id="APPLICATION">%2$s</xliff:g> ne répond pas.\n\nVoulez-vous quitter ?"</string> <string name="anr_activity_process" msgid="5776209883299089767">"L\'activité <xliff:g id="ACTIVITY">%1$s</xliff:g> ne répond pas.\n\nVoulez-vous quitter ?"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index b139842676c6..e39c52fc9448 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -1039,8 +1039,8 @@ <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB ל-MIDI"</string> <string name="usb_accessory_notification_title" msgid="7848236974087653666">"מחובר לאביזר USB"</string> <string name="usb_notification_message" msgid="7347368030849048437">"גע להצגת עוד אפשרויות."</string> - <string name="adb_active_notification_title" msgid="6729044778949189918">"ניקוי באגים של USB מחובר"</string> - <string name="adb_active_notification_message" msgid="1016654627626476142">"גע כדי להשבית ניקוי באגים בהתקן ה-USB."</string> + <string name="adb_active_notification_title" msgid="6729044778949189918">"ניפוי באגים של USB מחובר"</string> + <string name="adb_active_notification_message" msgid="1016654627626476142">"גע כדי להשבית ניפוי באגים בהתקן ה-USB."</string> <string name="select_input_method" msgid="8547250819326693584">"שינוי מקלדת"</string> <string name="configure_input_methods" msgid="4769971288371946846">"בחר מקלדות"</string> <string name="show_ime" msgid="9157568568695230830">"הצג שיטת קלט"</string> diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml index 3aefd671ac0a..c410d19c1757 100644 --- a/core/res/res/values-ky-rKG/strings.xml +++ b/core/res/res/values-ky-rKG/strings.xml @@ -898,7 +898,7 @@ <string name="aerr_title" msgid="1905800560317137752"></string> <string name="aerr_application" msgid="932628488013092776">"Тилекке каршы, <xliff:g id="APPLICATION">%1$s</xliff:g> токтотулду."</string> <string name="aerr_process" msgid="4507058997035697579">"Тилекке каршы, <xliff:g id="PROCESS">%1$s</xliff:g> процесси токтотулду."</string> - <string name="aerr_process_silence" msgid="4226685530196000222">"<xliff:g id="PROCESS">%1$s</xliff:g> өчүрүп-күйгүзгөнгө чейин бузулду."</string> + <string name="aerr_process_silence" msgid="4226685530196000222">"Жымжырттык режиминде <xliff:g id="PROCESS">%1$s</xliff:g> колдонмосун иштетип жатканда ката кетиши мүмкүн. Ал ката түзмөктү өчүрүп-күйгүзгөндөн кийин жоюлат."</string> <string name="anr_title" msgid="4351948481459135709"></string> <string name="anr_activity_application" msgid="1904477189057199066">"<xliff:g id="APPLICATION">%2$s</xliff:g> жооп бербей жатат.\n\nЖабылсынбы?"</string> <string name="anr_activity_process" msgid="5776209883299089767">"<xliff:g id="ACTIVITY">%1$s</xliff:g> аракети жооп бербей жатат.\n\nЖабылсынбы?"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index e79656f92c12..56e2cf2fad79 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -898,7 +898,7 @@ <string name="aerr_title" msgid="1905800560317137752"></string> <string name="aerr_application" msgid="932628488013092776">"<xliff:g id="APPLICATION">%1$s</xliff:g> har dessverre stoppet."</string> <string name="aerr_process" msgid="4507058997035697579">"Prosessen <xliff:g id="PROCESS">%1$s</xliff:g> har dessverre stoppet."</string> - <string name="aerr_process_silence" msgid="4226685530196000222">"«Silence» kræsjer som følge av <xliff:g id="PROCESS">%1$s</xliff:g>, frem til den starter på nytt."</string> + <string name="aerr_process_silence" msgid="4226685530196000222">"Appen Silence kræsjer som følge av <xliff:g id="PROCESS">%1$s</xliff:g>, frem til omstart."</string> <string name="anr_title" msgid="4351948481459135709"></string> <string name="anr_activity_application" msgid="1904477189057199066">"<xliff:g id="APPLICATION">%2$s</xliff:g> svarer ikke.\n\nVil du lukke appen?"</string> <string name="anr_activity_process" msgid="5776209883299089767">"Aktiviteten <xliff:g id="ACTIVITY">%1$s</xliff:g> svarer ikke.\n\nVil du lukke den?"</string> diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml index 1cb39f0de592..7399fa9d58ce 100644 --- a/core/res/res/values/colors_material.xml +++ b/core/res/res/values/colors_material.xml @@ -41,9 +41,6 @@ <color name="switch_thumb_disabled_material_dark">#ff616161</color> <color name="switch_thumb_disabled_material_light">#ffbdbdbd</color> - <color name="link_text_material_light">@color/material_deep_teal_500</color> - <color name="link_text_material_dark">@color/material_deep_teal_200</color> - <!-- Text & foreground colors --> <eat-comment /> diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml index 295b453fb0b7..e406ae06d96c 100644 --- a/core/res/res/values/themes_material.xml +++ b/core/res/res/values/themes_material.xml @@ -67,8 +67,8 @@ please see themes_device_defaults.xml. <item name="textColorHintInverse">@color/hint_foreground_material_light</item> <item name="textColorHighlight">@color/highlighted_text_material</item> <item name="textColorHighlightInverse">@color/highlighted_text_material</item> - <item name="textColorLink">@color/link_text_material_dark</item> - <item name="textColorLinkInverse">@color/link_text_material_light</item> + <item name="textColorLink">?attr/colorAccent</item> + <item name="textColorLinkInverse">?attr/colorAccent</item> <item name="textColorSearchUrl">@color/search_url_text_material_dark</item> <item name="textColorAlertDialogListItem">@color/primary_text_material_dark</item> @@ -422,8 +422,8 @@ please see themes_device_defaults.xml. <item name="textColorHintInverse">@color/hint_foreground_material_dark</item> <item name="textColorHighlight">@color/highlighted_text_material</item> <item name="textColorHighlightInverse">@color/highlighted_text_material</item> - <item name="textColorLink">@color/link_text_material_light</item> - <item name="textColorLinkInverse">@color/link_text_material_dark</item> + <item name="textColorLink">?attr/colorAccent</item> + <item name="textColorLinkInverse">?attr/colorAccent</item> <item name="textColorSearchUrl">@color/search_url_text_material_light</item> <item name="textColorAlertDialogListItem">@color/primary_text_material_light</item> @@ -793,8 +793,6 @@ please see themes_device_defaults.xml. <item name="textColorHintInverse">@color/hint_foreground_material_dark</item> <item name="textColorHighlight">@color/highlighted_text_material</item> <item name="textColorHighlightInverse">@color/highlighted_text_material</item> - <item name="textColorLink">@color/link_text_material_light</item> - <item name="textColorLinkInverse">@color/link_text_material_dark</item> <item name="textColorSearchUrl">@color/search_url_text_material_light</item> <item name="textColorAlertDialogListItem">@color/primary_text_material_light</item> @@ -827,8 +825,6 @@ please see themes_device_defaults.xml. <item name="textColorHintInverse">@color/hint_foreground_material_light</item> <item name="textColorHighlight">@color/highlighted_text_material</item> <item name="textColorHighlightInverse">@color/highlighted_text_material</item> - <item name="textColorLink">@color/link_text_material_dark</item> - <item name="textColorLinkInverse">@color/link_text_material_light</item> <item name="textColorSearchUrl">@color/search_url_text_material_dark</item> <item name="textColorAlertDialogListItem">@color/primary_text_material_dark</item> diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 2dc1c96259c0..1c39ffef6fd4 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -1545,7 +1545,7 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector<AssetDir::FileInfo>* pMerg */ int dirNameLen = dirName.length(); void *iterationCookie; - if (!pZip->startIteration(&iterationCookie)) { + if (!pZip->startIteration(&iterationCookie, dirName.string(), NULL)) { ALOGW("ZipFileRO::startIteration returned false"); return false; } @@ -1560,9 +1560,7 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector<AssetDir::FileInfo>* pMerg continue; } //printf("Comparing %s in %s?\n", nameBuf, dirName.string()); - if (dirNameLen == 0 || - (strncmp(nameBuf, dirName.string(), dirNameLen) == 0 && - nameBuf[dirNameLen] == '/')) + if (dirNameLen == 0 || nameBuf[dirNameLen] == '/') { const char* cp; const char* nextSlash; diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 3c459d83cb50..6c224e5c35e6 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.ImageFormat; import android.graphics.Rect; +import android.graphics.SurfaceTexture; import android.media.MediaCodecInfo.CodecCapabilities; import android.os.Bundle; import android.os.Handler; @@ -32,6 +33,7 @@ import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.nio.ReadOnlyBufferException; import java.util.Arrays; import java.util.HashMap; @@ -228,8 +230,9 @@ import java.util.Map; data and submit it as a single codec-config buffer. <p> Android uses the following codec-specific data buffers. These are also required to be set in - the track format for proper {@link MediaMuxer} track configuration. Each parameter set and - codec-specific-data must start with a start code of {@code "\x00\x00\x00\x01"}. + the track format for proper {@link MediaMuxer} track configuration. Each parameter set and the + codec-specific-data sections marked with (<sup>*</sup>) must start with a start code of + {@code "\x00\x00\x00\x01"}. <p> <style>td.NA { background: #ccc; } .mid > tr > td { vertical-align: middle; }</style> <table> @@ -237,28 +240,48 @@ import java.util.Map; <th>Format</th> <th>CSD buffer #0</th> <th>CSD buffer #1</th> + <th>CSD buffer #2</th> </thead> <tbody class=mid> <tr> <td>AAC</td> - <td>Decoder-specific information from ESDS</td> + <td>Decoder-specific information from ESDS<sup>*</sup></td> <td class=NA>Not Used</td> + <td class=NA>Not Used</td> + </tr> + <tr> + <td>VORBIS</td> + <td>Identification header</td> + <td>Setup header</td> + <td class=NA>Not Used</td> + </tr> + <tr> + <td>OPUS</td> + <td>Identification header</td> + <td>Pre-skip in nanosecs<br> + (unsigned 64-bit {@linkplain ByteOrder#nativeOrder native-order} integer.)<br> + This overrides the pre-skip value in the identification header.</td> + <td>Seek Pre-roll in nanosecs<br> + (unsigned 64-bit {@linkplain ByteOrder#nativeOrder native-order} integer.)</td> </tr> <tr> <td>MPEG-4</td> - <td>Decoder-specific information from ESDS</td> + <td>Decoder-specific information from ESDS<sup>*</sup></td> + <td class=NA>Not Used</td> <td class=NA>Not Used</td> </tr> <tr> <td>H.264 AVC</td> - <td>SPS (Sequence Parameter Sets)</td> - <td>PPS (Picture Parameter Sets)</td> + <td>SPS (Sequence Parameter Sets<sup>*</sup>)</td> + <td>PPS (Picture Parameter Sets<sup>*</sup>)</td> + <td class=NA>Not Used</td> </tr> <tr> <td>H.265 HEVC</td> - <td>VPS (Video Parameter Sets) +<br> - SPS (Sequence Parameter Sets) +<br> - PPS (Picture Parameter Sets)</td> + <td>VPS (Video Parameter Sets<sup>*</sup>) +<br> + SPS (Sequence Parameter Sets<sup>*</sup>) +<br> + PPS (Picture Parameter Sets<sup>*</sup>)</td> + <td class=NA>Not Used</td> <td class=NA>Not Used</td> </tr> </tbody> @@ -297,10 +320,10 @@ import java.util.Map; releaseOutputBuffer} methods to return the buffer to the codec. <p> While you are not required to resubmit/release buffers immediately to the codec, holding onto - input and/or output buffers may stall the codec, and this behavior is device dependent. E.g. it - is possible that a codec may hold off on generating output buffers until all outstanding buffers - have been released/resubmitted. Therefore, try to hold onto to available buffers as little as - possible. + input and/or output buffers may stall the codec, and this behavior is device dependent. + <strong>Specifically, it is possible that a codec may hold off on generating output buffers until + <em>all</em> outstanding buffers have been released/resubmitted.</strong> Therefore, try to + hold onto to available buffers as little as possible. <p> Depending on the API version, you can process data in three ways: <table> @@ -346,7 +369,7 @@ import java.util.Map; <p> MediaCodec is typically used like this in asynchronous mode: <pre class=prettyprint> - MediaCodec codec = MediaCodec.createCodecByName(name); + MediaCodec codec = MediaCodec.createByCodecName(name); MediaFormat mOutputFormat; // member variable codec.setCallback(new MediaCodec.Callback() { {@literal @Override} @@ -403,7 +426,7 @@ import java.util.Map; <p> MediaCodec is typically used like this in synchronous mode: <pre> - MediaCodec codec = MediaCodec.createCodecByName(name); + MediaCodec codec = MediaCodec.createByCodecName(name); codec.configure(format, …); MediaFormat outputFormat = codec.getOutputFormat(); // option B codec.start(); @@ -442,7 +465,7 @@ import java.util.Map; between the size of the arrays and the number of input and output buffers used by the system, although the array size provides an upper bound. <pre> - MediaCodec codec = MediaCodec.createCodecByName(name); + MediaCodec codec = MediaCodec.createByCodecName(name); codec.configure(format, …); codec.start(); ByteBuffer[] inputBuffers = codec.getInputBuffers(); @@ -643,10 +666,10 @@ import java.util.Map; class. For API version numbers, see {@link android.os.Build.VERSION_CODES}. <style> - .api > tr > th, td { text-align: center; padding: 4px 4px; } + .api > tr > th, .api > tr > td { text-align: center; padding: 4px 4px; } .api > tr > th { vertical-align: bottom; } .api > tr > td { vertical-align: middle; } - .sml > tr > th, td { text-align: center; padding: 2px 4px; } + .sml > tr > th, .sml > tr > td { text-align: center; padding: 2px 4px; } .fn { text-align: left; } .fn > code > a { font: 14px/19px Roboto Condensed, sans-serif; } .deg45 { @@ -1561,7 +1584,7 @@ final public class MediaCodec { private boolean mHasSurface = false; /** - * Instantiate a decoder supporting input data of the given mime type. + * Instantiate the preferred decoder supporting input data of the given mime type. * * The following is a partial list of defined mime types and their semantics: * <ul> @@ -1580,6 +1603,10 @@ final public class MediaCodec { * <li>"audio/g711-mlaw" - G.711 ulaw audio * </ul> * + * <strong>Note:</strong> It is preferred to use {@link MediaCodecList#findDecoderForFormat} + * and {@link #createByCodecName} to ensure that the resulting codec can handle a + * given format. + * * @param type The mime type of the input data. * @throws IOException if the codec cannot be created. * @throws IllegalArgumentException if type is not a valid mime type. @@ -1592,7 +1619,12 @@ final public class MediaCodec { } /** - * Instantiate an encoder supporting output data of the given mime type. + * Instantiate the preferred encoder supporting output data of the given mime type. + * + * <strong>Note:</strong> It is preferred to use {@link MediaCodecList#findEncoderForFormat} + * and {@link #createByCodecName} to ensure that the resulting codec can handle a + * given format. + * * @param type The desired mime type of the output data. * @throws IOException if the codec cannot be created. * @throws IllegalArgumentException if type is not a valid mime type. @@ -1661,6 +1693,8 @@ final public class MediaCodec { private native final void native_reset(); /** + * Free up resources used by the codec instance. + * * Make sure you call this when you're done to free up any opened * component instance instead of relying on the garbage collector * to do this for you at some point in the future. @@ -1881,17 +1915,25 @@ final public class MediaCodec { private native final void native_stop(); /** - * Flush both input and output ports of the component, all indices - * previously returned in calls to {@link #dequeueInputBuffer} and - * {@link #dequeueOutputBuffer} become invalid. + * Flush both input and output ports of the component. * <p> - * If codec is configured in asynchronous mode, call {@link #start} - * after {@code flush} has returned to resume codec operations. The - * codec will not request input buffers until this has happened. + * Upon return, all indices previously returned in calls to {@link #dequeueInputBuffer + * dequeueInputBuffer} and {@link #dequeueOutputBuffer dequeueOutputBuffer} — or obtained + * via {@link Callback#onInputBufferAvailable onInputBufferAvailable} or + * {@link Callback#onOutputBufferAvailable onOutputBufferAvailable} callbacks — become + * invalid, and all buffers are owned by the codec. * <p> - * If codec is configured in synchronous mode, codec will resume - * automatically if an input surface was created. Otherwise, it - * will resume when {@link #dequeueInputBuffer} is called. + * If the codec is configured in asynchronous mode, call {@link #start} + * after {@code flush} has returned to resume codec operations. The codec + * will not request input buffers until this has happened. + * <strong>Note, however, that there may still be outstanding {@code onOutputBufferAvailable} + * callbacks that were not handled prior to calling {@code flush}. + * The indices returned via these callbacks also become invalid upon calling {@code flush} and + * should be discarded.</strong> + * <p> + * If the codec is configured in synchronous mode, codec will resume + * automatically if it is configured with an input surface. Otherwise, it + * will resume when {@link #dequeueInputBuffer dequeueInputBuffer} is called. * * @throws IllegalStateException if not in the Executing state. * @throws MediaCodec.CodecException upon codec error. @@ -2082,6 +2124,15 @@ final public class MediaCodec { * To indicate that this is the final piece of input data (or rather that * no more input data follows unless the decoder is subsequently flushed) * specify the flag {@link #BUFFER_FLAG_END_OF_STREAM}. + * <p class=note> + * <strong>Note:</strong> Prior to {@link android.os.Build.VERSION_CODES#M}, + * {@code presentationTimeUs} was not propagated to the frame timestamp of (rendered) + * Surface output buffers, and the resulting frame timestamp was undefined. + * Use {@link #releaseOutputBuffer(int, long)} to ensure a specific frame timestamp is set. + * Similarly, since frame timestamps can be used by the destination surface for rendering + * synchronization, <strong>care must be taken to normalize presentationTimeUs so as to not be + * mistaken for a system time. (See {@linkplain #releaseOutputBuffer(int, long) + * SurfaceView specifics}).</strong> * * @param index The index of a client-owned input buffer previously returned * in a call to {@link #dequeueInputBuffer}. @@ -2089,7 +2140,10 @@ final public class MediaCodec { * @param size The number of bytes of valid input data. * @param presentationTimeUs The presentation timestamp in microseconds for this * buffer. This is normally the media time at which this - * buffer should be presented (rendered). + * buffer should be presented (rendered). When using an output + * surface, this will be propagated as the {@link + * SurfaceTexture#getTimestamp timestamp} for the frame (after + * conversion to nanoseconds). * @param flags A bitmask of flags * {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}. * While not prohibited, most codecs do not use the @@ -2202,8 +2256,10 @@ final public class MediaCodec { }; /** - * Similar to {@link #queueInputBuffer} but submits a buffer that is + * Similar to {@link #queueInputBuffer queueInputBuffer} but submits a buffer that is * potentially encrypted. + * <strong>Check out further notes at {@link #queueInputBuffer queueInputBuffer}.</strong> + * * @param index The index of a client-owned input buffer previously returned * in a call to {@link #dequeueInputBuffer}. * @param offset The byte offset into the input buffer at which the data starts. @@ -2310,7 +2366,7 @@ final public class MediaCodec { /** * Dequeue an output buffer, block at most "timeoutUs" microseconds. * Returns the index of an output buffer that has been successfully - * decoded or one of the INFO_* constants below. + * decoded or one of the INFO_* constants. * @param info Will be filled with buffer meta data. * @param timeoutUs The timeout in microseconds, a negative timeout indicates "infinite". * @throws IllegalStateException if not in the Executing state, @@ -2338,9 +2394,11 @@ final public class MediaCodec { @NonNull BufferInfo info, long timeoutUs); /** - * If you are done with a buffer, use this call to return the buffer to - * the codec. If you previously specified a surface when configuring this - * video decoder you can optionally render the buffer. + * If you are done with a buffer, use this call to return the buffer to the codec + * or to render it on the output surface. If you configured the codec with an + * output surface, setting {@code render} to {@code true} will first send the buffer + * to that output surface. The surface will release the buffer back to the codec once + * it is no longer used/displayed. * * Once an output buffer is released to the codec, it MUST NOT * be used until it is later retrieved by {@link #getOutputBuffer} in response @@ -2674,6 +2732,8 @@ final public class MediaCodec { * <b>Note:</b> As of API 21, dequeued input buffers are * automatically {@link java.nio.Buffer#clear cleared}. * + * <em>Do not use this method if using an input surface.</em> + * * @throws IllegalStateException if not in the Executing state, * or codec is configured in asynchronous mode. * @throws MediaCodec.CodecException upon codec error. @@ -2703,6 +2763,8 @@ final public class MediaCodec { * buffers that are dequeued will be set to the valid data * range. * + * <em>Do not use this method if using an output surface.</em> + * * @throws IllegalStateException if not in the Executing state, * or codec is configured in asynchronous mode. * @throws MediaCodec.CodecException upon codec error. @@ -2988,6 +3050,10 @@ final public class MediaCodec { /** * Called when an output frame has rendered on the output surface. + * <p> + * <strong>Note:</strong> This callback is for informational purposes only: to get precise + * render timing samples, and can be significantly delayed and batched. Some frames may have + * been rendered even if there was no callback generated. * * @param codec the MediaCodec instance * @param presentationTimeUs the presentation time (media time) of the frame rendered. @@ -3004,10 +3070,14 @@ final public class MediaCodec { } /** - * Register a callback to be invoked when an output frame is rendered on the output surface. + * Registers a callback to be invoked when an output frame is rendered on the output surface. * <p> * This method can be called in any codec state, but will only have an effect in the * Executing state for codecs that render buffers to the output surface. + * <p> + * <strong>Note:</strong> This callback is for informational purposes only: to get precise + * render timing samples, and can be significantly delayed and batched. Some frames may have + * been rendered even if there was no callback generated. * * @param listener the callback that will be run * @param handler the callback will be run on the handler's thread. If {@code null}, diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 8243d4041da5..41019357a0e4 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -525,6 +525,14 @@ public final class MediaCodecInfo { /** * Query whether codec supports a given {@link MediaFormat}. + * + * <p class=note> + * <strong>Note:</strong> On {@link android.os.Build.VERSION_CODES#LOLLIPOP}, + * {@code format} must not contain a {@linkplain MediaFormat#KEY_FRAME_RATE + * frame rate}. Use + * <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code> + * to clear any existing frame rate setting in the format. + * * @param format media format with optional feature directives. * @throws IllegalArgumentException if format is not a valid media format. * @return whether the codec capabilities support the given format @@ -1230,8 +1238,22 @@ public final class MediaCodecInfo { * May return {@code null}, if the codec did not publish any measurement * data. * <p> - * This is a performance estimate, based on full-speed decoding - * and encoding measurements of common video sizes supported by the codec. + * This is a performance estimate provided by the device manufacturer + * based on full-speed decoding and encoding measurements in various configurations + * of common video sizes supported by the codec. As such it should only be used to + * compare individual codecs on the device. The value is not suitable for comparing + * different devices or even different android releases for the same device. + * <p> + * The returned range corresponds to the fastest frame rates achieved in the tested + * configurations. It is interpolated from the nearest frame size(s) tested. Codec + * performance is severely impacted by other activity on the device, and can vary + * significantly. + * <p class=note> + * Use this method in cases where only codec performance matters, e.g. to evaluate if + * a codec has any chance of meeting a performance target. Codecs are listed + * in {@link MediaCodecList} in the preferred order as defined by the device + * manufacturer. As such, applications should use the first suitable codec in the + * list to achieve the best balance between power use and performance. * * @param width the width of the video * @param height the height of the video diff --git a/media/java/android/media/MediaCodecList.java b/media/java/android/media/MediaCodecList.java index f44e048397d4..cd7b3d35a590 100644 --- a/media/java/android/media/MediaCodecList.java +++ b/media/java/android/media/MediaCodecList.java @@ -190,6 +190,13 @@ final public class MediaCodecList { * Find a decoder supporting a given {@link MediaFormat} in the list * of media-codecs. * + * <p class=note> + * <strong>Note:</strong> On {@link android.os.Build.VERSION_CODES#LOLLIPOP}, + * {@code format} must not contain a {@linkplain MediaFormat#KEY_FRAME_RATE + * frame rate}. Use + * <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code> + * to clear any existing frame rate setting in the format. + * * @param format A decoder media format with optional feature directives. * @throws IllegalArgumentException if format is not a valid media format. * @throws NullPointerException if format is null. @@ -204,6 +211,13 @@ final public class MediaCodecList { * Find an encoder supporting a given {@link MediaFormat} in the list * of media-codecs. * + * <p class=note> + * <strong>Note:</strong> On {@link android.os.Build.VERSION_CODES#LOLLIPOP}, + * {@code format} must not contain a {@linkplain MediaFormat#KEY_FRAME_RATE + * frame rate}. Use + * <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code> + * to clear any existing frame rate setting in the format. + * * @param format An encoder media format with optional feature directives. * @throws IllegalArgumentException if format is not a valid media format. * @throws NullPointerException if format is null. diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java index 9e9d60283ddf..c2bcd930dd29 100644 --- a/media/java/android/media/Ringtone.java +++ b/media/java/android/media/Ringtone.java @@ -52,7 +52,8 @@ public class Ringtone { MediaStore.Audio.Media.TITLE }; /** Selection that limits query results to just audio files */ - private static final String MEDIA_SELECTION = MediaColumns.MIME_TYPE + " LIKE 'audio/%'"; + private static final String MEDIA_SELECTION = MediaColumns.MIME_TYPE + " LIKE 'audio/%' OR " + + MediaColumns.MIME_TYPE + " IN ('application/ogg', 'application/x-flac')"; // keep references on active Ringtones until stopped or completion listener called. private static final ArrayList<Ringtone> sActiveRingtones = new ArrayList<Ringtone>(); diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java index 40aa59cf5e8d..a813ce76985f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java @@ -607,9 +607,6 @@ abstract class BaseActivity extends Activity { onCurrentDirectoryChanged(ANIM_NONE); onStackRestored(mRestoredStack, mExternal); - - getDisplayState().restored = true; - onCurrentDirectoryChanged(ANIM_NONE); } } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java index 0c6837fcc60c..8f792de32db9 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java @@ -53,15 +53,18 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout super(context, attrs); } + @Override public void setKeyguardCallback(KeyguardSecurityCallback callback) { mCallback = callback; } + @Override public void setLockPatternUtils(LockPatternUtils utils) { mLockPatternUtils = utils; mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled(); } + @Override public void reset() { // start fresh resetPasswordText(false /* animate */); @@ -95,6 +98,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout } } + @Override public void onEmergencyButtonClickedWhenInCall() { mCallback.reset(); } @@ -115,11 +119,11 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout mPendingLockCheck.cancel(false); } - if (entry.length() < MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) { + if (entry.length() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) { // to avoid accidental lockout, only count attempts that are long enough to be a // real password. This may require some tweaking. setPasswordEntryInputEnabled(true); - onPasswordChecked(entry, false, 0); + onPasswordChecked(false /* matched */, 0, false /* not valid - too short */); return; } @@ -132,24 +136,27 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout public void onChecked(boolean matched, int timeoutMs) { setPasswordEntryInputEnabled(true); mPendingLockCheck = null; - onPasswordChecked(entry, matched, timeoutMs); + onPasswordChecked(matched, timeoutMs, true /* isValidPassword */); } }); } - private void onPasswordChecked(String entry, boolean matched, int timeoutMs) { + private void onPasswordChecked(boolean matched, int timeoutMs, boolean isValidPassword) { if (matched) { mCallback.reportUnlockAttempt(true, 0); mCallback.dismiss(true); } else { - mCallback.reportUnlockAttempt(false, timeoutMs); - int attempts = KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(); - if (timeoutMs > 0) { - long deadline = mLockPatternUtils.setLockoutAttemptDeadline( - KeyguardUpdateMonitor.getCurrentUser(), timeoutMs); - handleAttemptLockout(deadline); + if (isValidPassword) { + mCallback.reportUnlockAttempt(false, timeoutMs); + if (timeoutMs > 0) { + long deadline = mLockPatternUtils.setLockoutAttemptDeadline( + KeyguardUpdateMonitor.getCurrentUser(), timeoutMs); + handleAttemptLockout(deadline); + } + } + if (timeoutMs == 0) { + mSecurityMessageDisplay.setMessage(getWrongPasswordStringId(), true); } - mSecurityMessageDisplay.setMessage(getWrongPasswordStringId(), true); } resetPasswordText(true /* animate */); } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java index b000e26a7658..4bd1a2ed29fc 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java @@ -82,6 +82,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit * Useful for clearing out the wrong pattern after a delay */ private Runnable mCancelPatternRunnable = new Runnable() { + @Override public void run() { mLockPatternView.clearPattern(); } @@ -117,10 +118,12 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit R.dimen.disappear_y_translation); } + @Override public void setKeyguardCallback(KeyguardSecurityCallback callback) { mCallback = callback; } + @Override public void setLockPatternUtils(LockPatternUtils utils) { mLockPatternUtils = utils; } @@ -153,6 +156,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit } } + @Override public void onEmergencyButtonClickedWhenInCall() { mCallback.reset(); } @@ -174,6 +178,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit return result; } + @Override public void reset() { // reset lock pattern mLockPatternView.enableInput(); @@ -207,18 +212,22 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit private class UnlockPatternListener implements LockPatternView.OnPatternListener { + @Override public void onPatternStart() { mLockPatternView.removeCallbacks(mCancelPatternRunnable); mSecurityMessageDisplay.setMessage("", false); } + @Override public void onPatternCleared() { } + @Override public void onPatternCellAdded(List<LockPatternView.Cell> pattern) { mCallback.userActivity(); } + @Override public void onPatternDetected(final List<LockPatternView.Cell> pattern) { mLockPatternView.disableInput(); if (mPendingLockCheck != null) { @@ -227,7 +236,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) { mLockPatternView.enableInput(); - onPatternChecked(pattern, false, 0); + onPatternChecked(false, 0, false /* not valid - too short */); return; } @@ -240,29 +249,30 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit public void onChecked(boolean matched, int timeoutMs) { mLockPatternView.enableInput(); mPendingLockCheck = null; - onPatternChecked(pattern, matched, timeoutMs); + onPatternChecked(matched, timeoutMs, true); } }); + if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) { + mCallback.userActivity(); + } } - private void onPatternChecked(List<LockPatternView.Cell> pattern, boolean matched, - int timeoutMs) { + private void onPatternChecked(boolean matched, int timeoutMs, boolean isValidPattern) { if (matched) { mCallback.reportUnlockAttempt(true, 0); mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct); mCallback.dismiss(true); } else { - if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) { - mCallback.userActivity(); - } mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong); - mCallback.reportUnlockAttempt(false, timeoutMs); - int attempts = mKeyguardUpdateMonitor.getFailedUnlockAttempts(); - if (timeoutMs > 0) { - long deadline = mLockPatternUtils.setLockoutAttemptDeadline( - KeyguardUpdateMonitor.getCurrentUser(), timeoutMs); - handleAttemptLockout(deadline); - } else { + if (isValidPattern) { + mCallback.reportUnlockAttempt(false, timeoutMs); + if (timeoutMs > 0) { + long deadline = mLockPatternUtils.setLockoutAttemptDeadline( + KeyguardUpdateMonitor.getCurrentUser(), timeoutMs); + handleAttemptLockout(deadline); + } + } + if (timeoutMs == 0) { mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pattern, true); mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS); } diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index c7720dee9c32..bf8a41d6fbee 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -62,11 +62,11 @@ <string name="label_view" msgid="6304565553218192990">"הצג"</string> <string name="always_use_device" msgid="1450287437017315906">"השתמש כברירת מחדל עבור מכשיר USB זה"</string> <string name="always_use_accessory" msgid="1210954576979621596">"השתמש כברירת מחדל עבור אביזר USB זה"</string> - <string name="usb_debugging_title" msgid="4513918393387141949">"האם לאפשר ניקוי באגים ב-USB?"</string> + <string name="usb_debugging_title" msgid="4513918393387141949">"האם לאפשר ניפוי באגים ב-USB?"</string> <string name="usb_debugging_message" msgid="2220143855912376496">"טביעת האצבע של מפתח ה-RSA של המחשב היא:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string> <string name="usb_debugging_always" msgid="303335496705863070">"אפשר תמיד ממחשב זה"</string> - <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"לא ניתן לבצע ניקוי באגים ב-USB"</string> - <string name="usb_debugging_secondary_user_message" msgid="8572228137833020196">"המשתמש הנוכחי שמחובר למערכת במכשיר לא יכול להפעיל ניקוי באגים ב-USB. כדי להשתמש בתכונה זו, יש להיכנס כמנהל מערכת."</string> + <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"לא ניתן לבצע ניפוי באגים ב-USB"</string> + <string name="usb_debugging_secondary_user_message" msgid="8572228137833020196">"המשתמש הנוכחי שמחובר למערכת במכשיר לא יכול להפעיל ניפוי באגים ב-USB. כדי להשתמש בתכונה זו, יש להיכנס כמנהל מערכת."</string> <string name="compat_mode_on" msgid="6623839244840638213">"הגדל תצוגה כדי למלא את המסך"</string> <string name="compat_mode_off" msgid="4434467572461327898">"מתח כדי למלא את המסך"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"שומר צילום מסך..."</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 4b2eefc630c5..70308ce8cc93 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -345,7 +345,7 @@ <string name="guest_notification_text" msgid="335747957734796689">"Fjern gjesten for å slette appene og dataene"</string> <string name="guest_notification_remove_action" msgid="8820670703892101990">"FJERN GJEST"</string> <string name="user_logout_notification_title" msgid="1453960926437240727">"Logg ut bruker"</string> - <string name="user_logout_notification_text" msgid="3350262809611876284">"Logg ut nåværende bruker"</string> + <string name="user_logout_notification_text" msgid="3350262809611876284">"Logg av nåværende bruker"</string> <string name="user_logout_notification_action" msgid="1195428991423425062">"LOGG UT BRUKER"</string> <string name="user_add_user_title" msgid="4553596395824132638">"Vil du legge til en ny bruker?"</string> <string name="user_add_user_message_short" msgid="2161624834066214559">"Når du legger til en ny bruker, må vedkommende konfigurere sitt eget område.\n\nAlle brukere kan oppdatere apper for alle andre brukere."</string> diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml index 62390d70c4ae..8def6f42de55 100644 --- a/packages/SystemUI/res/values-uz-rUZ/strings.xml +++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml @@ -345,7 +345,7 @@ <string name="guest_notification_text" msgid="335747957734796689">"Ilova va ma’l-ni o‘chirish u-n mehmon hisobini o‘chiring"</string> <string name="guest_notification_remove_action" msgid="8820670703892101990">"MEHMON HISOBINI O‘CHIRISH"</string> <string name="user_logout_notification_title" msgid="1453960926437240727">"Foydalanuvchi nomidan chiqish"</string> - <string name="user_logout_notification_text" msgid="3350262809611876284">"Joriy foydalanuvchini tizimdan chiqarish"</string> + <string name="user_logout_notification_text" msgid="3350262809611876284">"Joriy foydalanuvchini tizimdan chiqaring"</string> <string name="user_logout_notification_action" msgid="1195428991423425062">"FOYDALANUVCHI NOMIDAN CHIQISH"</string> <string name="user_add_user_title" msgid="4553596395824132638">"Yangi foyd-chi qo‘shilsinmi?"</string> <string name="user_add_user_message_short" msgid="2161624834066214559">"Yangi foydalanuvchi qo‘shilgach, o‘sha shaxs o‘z hududini sozlashi lozim bo‘ladi.\n\nHar qanday foydalanuvchi ilovalarni barcha foydalanuvchilar uchun yangilashi mumkin."</string> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 13e9b1631b14..ed1dca38780f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -51,7 +51,7 @@ public class MobileSignalController extends SignalController< @VisibleForTesting final PhoneStateListener mPhoneStateListener; // Save entire info for logging, we only use the id. - private final SubscriptionInfo mSubscriptionInfo; + final SubscriptionInfo mSubscriptionInfo; // @VisibleForDemoMode final SparseArray<MobileIconGroup> mNetworkToIconLookup; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 57dfff5877ac..29968081e758 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -66,6 +66,11 @@ public class NetworkControllerImpl extends BroadcastReceiver // additional diagnostics, but not logspew static final boolean CHATTY = Log.isLoggable(TAG + "Chat", Log.DEBUG); + private static final int EMERGENCY_NO_CONTROLLERS = 0; + private static final int EMERGENCY_FIRST_CONTROLLER = 100; + private static final int EMERGENCY_VOICE_CONTROLLER = 200; + private static final int EMERGENCY_NO_SUB = 300; + private final Context mContext; private final TelephonyManager mPhone; private final WifiManager mWifiManager; @@ -118,6 +123,9 @@ public class NetworkControllerImpl extends BroadcastReceiver // Handler that all callbacks are made on. private final CallbackHandler mCallbackHandler; + private int mEmergencySource; + private boolean mIsEmergency; + @VisibleForTesting ServiceState mLastServiceState; @@ -267,6 +275,7 @@ public class NetworkControllerImpl extends BroadcastReceiver if (mMobileSignalControllers.size() == 0) { // When there are no active subscriptions, determine emengency state from last // broadcast. + mEmergencySource = EMERGENCY_NO_CONTROLLERS; return mLastServiceState != null && mLastServiceState.isEmergencyOnly(); } int voiceSubId = mSubDefaults.getDefaultVoiceSubId(); @@ -274,16 +283,20 @@ public class NetworkControllerImpl extends BroadcastReceiver for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { if (!mobileSignalController.getState().isEmergency) { + mEmergencySource = EMERGENCY_FIRST_CONTROLLER + + mobileSignalController.mSubscriptionInfo.getSubscriptionId(); if (DEBUG) Log.d(TAG, "Found emergency " + mobileSignalController.mTag); return false; } } } if (mMobileSignalControllers.containsKey(voiceSubId)) { + mEmergencySource = EMERGENCY_VOICE_CONTROLLER + voiceSubId; if (DEBUG) Log.d(TAG, "Getting emergency from " + voiceSubId); return mMobileSignalControllers.get(voiceSubId).getState().isEmergency; } if (DEBUG) Log.e(TAG, "Cannot find controller for voice sub: " + voiceSubId); + mEmergencySource = EMERGENCY_NO_SUB + voiceSubId; // Something is wrong, better assume we can't make calls... return true; } @@ -293,7 +306,8 @@ public class NetworkControllerImpl extends BroadcastReceiver * so we should recheck and send out the state to listeners. */ void recalculateEmergency() { - mCallbackHandler.setEmergencyCallsOnly(isEmergencyOnly()); + mIsEmergency = isEmergencyOnly(); + mCallbackHandler.setEmergencyCallsOnly(mIsEmergency); } public void addSignalCallback(SignalCallback cb) { @@ -606,6 +620,10 @@ public class NetworkControllerImpl extends BroadcastReceiver pw.println(mLocale); pw.print(" mLastServiceState="); pw.println(mLastServiceState); + pw.print(" mIsEmergency="); + pw.println(mIsEmergency); + pw.print(" mEmergencySource="); + pw.println(emergencyToString(mEmergencySource)); for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { mobileSignalController.dump(pw); @@ -617,6 +635,19 @@ public class NetworkControllerImpl extends BroadcastReceiver mAccessPoints.dump(pw); } + private static final String emergencyToString(int emergencySource) { + if (emergencySource > EMERGENCY_NO_SUB) { + return "NO_SUB(" + (emergencySource - EMERGENCY_NO_SUB) + ")"; + } else if (emergencySource > EMERGENCY_VOICE_CONTROLLER) { + return "VOICE_CONTROLLER(" + (emergencySource - EMERGENCY_VOICE_CONTROLLER) + ")"; + } else if (emergencySource > EMERGENCY_FIRST_CONTROLLER) { + return "FIRST_CONTROLLER(" + (emergencySource - EMERGENCY_FIRST_CONTROLLER) + ")"; + } else if (emergencySource == EMERGENCY_NO_CONTROLLERS) { + return "NO_CONTROLLERS"; + } + return "UNKNOWN_SOURCE"; + } + private boolean mDemoMode; private boolean mDemoInetCondition; private WifiSignalController.WifiState mDemoWifiState; diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index ec027895ce99..2a3492b4f06b 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -1718,11 +1718,15 @@ public class AppOpsService extends IAppOpsService.Stub { } private static String[] getPackagesForUid(int uid) { + String[] packageNames = null; try { - return AppGlobals.getPackageManager().getPackagesForUid(uid); + packageNames= AppGlobals.getPackageManager().getPackagesForUid(uid); } catch (RemoteException e) { /* ignore - local call */ } - return EmptyArray.STRING; + if (packageNames == null) { + return EmptyArray.STRING; + } + return packageNames; } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7b50999a90eb..f9812687662c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1278,7 +1278,7 @@ public final class ActivityManagerService extends ActivityManagerNative int mTargetUserId = UserHandle.USER_NULL; // If there are multiple profiles for the current user, their ids are here // Currently only the primary user can have managed profiles - int[] mCurrentProfileIds = new int[] {UserHandle.USER_OWNER}; // Accessed by ActivityStack + int[] mCurrentProfileIds = new int[] {}; // Accessed by ActivityStack /** * Mapping from each known user ID to the profile group ID it is associated with. diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java index 6ee165093e39..b2161149d663 100644 --- a/services/core/java/com/android/server/am/RecentTasks.java +++ b/services/core/java/com/android/server/am/RecentTasks.java @@ -435,7 +435,8 @@ class RecentTasks extends ArrayList<TaskRecord> { */ int trimForTaskLocked(TaskRecord task, boolean doTrim) { int recentsCount = size(); - final boolean document = task.intent != null && task.intent.isDocument(); + final Intent intent = task.intent; + final boolean document = intent != null && intent.isDocument(); int maxRecents = task.maxRecents - 1; for (int i = 0; i < recentsCount; i++) { final TaskRecord tr = get(i); @@ -446,12 +447,13 @@ class RecentTasks extends ArrayList<TaskRecord> { if (i > MAX_RECENT_BITMAPS) { tr.freeLastThumbnail(); } + final Intent trIntent = tr.intent; final boolean sameAffinity = task.affinity != null && task.affinity.equals(tr.affinity); - final boolean trIsDocument = tr.intent != null && tr.intent.isDocument(); + final boolean sameIntent = (intent != null && intent.filterEquals(trIntent)); + final boolean trIsDocument = trIntent != null && trIntent.isDocument(); final boolean bothDocuments = document && trIsDocument; - if (!sameAffinity && !bothDocuments) { - // Not the same affinity and not documents. Move along... + if (!sameAffinity && !sameIntent && !bothDocuments) { continue; } diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java index f7e435ef29ff..57ca552d353e 100644 --- a/services/core/java/com/android/server/location/GpsLocationProvider.java +++ b/services/core/java/com/android/server/location/GpsLocationProvider.java @@ -490,6 +490,12 @@ public class GpsLocationProvider implements LocationProviderInterface { private void checkSmsSuplInit(Intent intent) { SmsMessage[] messages = Intents.getMessagesFromIntent(intent); + + if (messages == null) { + Log.e(TAG, "Message does not exist in the intent."); + return; + } + for (int i=0; i <messages.length; i++) { byte[] supl_init = messages[i].getUserData(); native_agps_ni_message(supl_init,supl_init.length); diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java index 669b8e5b21be..3227ef857f5e 100644 --- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java @@ -696,7 +696,7 @@ final class DefaultPermissionGrantPolicy { List<PackageParser.Package> syncAdapterPackages = new ArrayList<>(); Intent homeIntent = new Intent(Intent.ACTION_MAIN); - homeIntent.addCategory(Intent.CATEGORY_HOME); + homeIntent.addCategory(Intent.CATEGORY_LAUNCHER); for (String syncAdapterPackageName : syncAdapterPackageNames) { homeIntent.setPackage(syncAdapterPackageName); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 4479a377802d..5598b51a1f71 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -66,6 +66,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageParser.isApkFile; import static android.os.Process.PACKAGE_INFO_GID; import static android.os.Process.SYSTEM_UID; +import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDWR; import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE; @@ -163,6 +164,7 @@ import android.os.SELinux; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.IMountService; @@ -1134,16 +1136,23 @@ public class PackageManagerService extends IPackageManager.Stub { // need to do anything. The pending install // will be processed later on. if (!mBound) { - // If this is the only one pending we might - // have to bind to the service again. - if (!connectToService()) { - Slog.e(TAG, "Failed to bind to media container service"); - params.serviceError(); - return; - } else { - // Once we bind to the service, the first - // pending request will be processed. - mPendingInstalls.add(idx, params); + try { + Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindMCS", + System.identityHashCode(params)); + // If this is the only one pending we might + // have to bind to the service again. + if (!connectToService()) { + Slog.e(TAG, "Failed to bind to media container service"); + params.serviceError(); + return; + } else { + // Once we bind to the service, the first + // pending request will be processed. + mPendingInstalls.add(idx, params); + } + } finally { + Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindMCS", + System.identityHashCode(params)); } } else { mPendingInstalls.add(idx, params); @@ -1168,6 +1177,8 @@ public class PackageManagerService extends IPackageManager.Stub { for (HandlerParams params : mPendingInstalls) { // Indicate service bind error params.serviceError(); + Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", + System.identityHashCode(params)); } mPendingInstalls.clear(); } else { @@ -1205,6 +1216,8 @@ public class PackageManagerService extends IPackageManager.Stub { } } } + Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", + System.identityHashCode(params)); } else { // Should never happen ideally. Slog.w(TAG, "Empty queue"); @@ -1222,6 +1235,8 @@ public class PackageManagerService extends IPackageManager.Stub { for (HandlerParams params : mPendingInstalls) { // Indicate service bind error params.serviceError(); + Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", + System.identityHashCode(params)); } mPendingInstalls.clear(); } @@ -1249,7 +1264,9 @@ public class PackageManagerService extends IPackageManager.Stub { } case MCS_GIVE_UP: { if (DEBUG_INSTALL) Slog.i(TAG, "mcs_giveup too many retries"); - mPendingInstalls.remove(0); + HandlerParams params = mPendingInstalls.remove(0); + Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", + System.identityHashCode(params)); break; } case SEND_PENDING_BROADCAST: { @@ -1444,6 +1461,8 @@ public class PackageManagerService extends IPackageManager.Stub { } else { Slog.e(TAG, "Bogus post-install token " + msg.arg1); } + + Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1); } break; case UPDATED_MEDIA_STATUS: { if (DEBUG_SD_INSTALL) Log.i(TAG, "Got message UPDATED_MEDIA_STATUS"); @@ -1525,6 +1544,8 @@ public class PackageManagerService extends IPackageManager.Stub { processPendingInstall(args, ret); mHandler.sendEmptyMessage(MCS_UNBIND); } + Trace.asyncTraceEnd( + TRACE_TAG_PACKAGE_MANAGER, "pendingVerification", verificationId); break; } case PACKAGE_VERIFIED: { @@ -2207,7 +2228,7 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.enableSystemPackageLPw(packageName); try { - scanPackageLI(scanFile, reparseFlags, scanFlags, 0, null); + scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, null); } catch (PackageManagerException e) { Slog.e(TAG, "Failed to parse original system package: " + e.getMessage()); @@ -5577,7 +5598,7 @@ public class PackageManagerService extends IPackageManager.Stub { continue; } try { - scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK, + scanPackageTracedLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK, scanFlags, currentTime, null); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage()); @@ -5664,9 +5685,23 @@ public class PackageManagerService extends IPackageManager.Stub { } } - /* - * Scan a package and return the newly parsed package. - * Returns null in case of errors and the error code is stored in mLastScanError + /** + * Traces a package scan. + * @see #scanPackageLI(File, int, int, long, UserHandle) + */ + private PackageParser.Package scanPackageTracedLI(File scanFile, int parseFlags, int scanFlags, + long currentTime, UserHandle user) throws PackageManagerException { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage"); + try { + return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + /** + * Scans a package and returns the newly parsed package. + * Returns {@code null} in case of errors and the error code is stored in mLastScanError */ private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { @@ -6437,6 +6472,16 @@ public class PackageManagerService extends IPackageManager.Stub { return cpuAbiOverride; } + private PackageParser.Package scanPackageTracedLI(PackageParser.Package pkg, int parseFlags, + int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage"); + try { + return scanPackageLI(pkg, parseFlags, scanFlags, currentTime, user); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { boolean success = false; @@ -6984,13 +7029,18 @@ public class PackageManagerService extends IPackageManager.Stub { // this symlink for 64 bit libraries. if (pkg.applicationInfo.primaryCpuAbi != null && !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) { - final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir; - for (int userId : userIds) { - if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName, - nativeLibPath, userId) < 0) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Failed linking native library dir (user=" + userId + ")"); + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "linkNativeLib"); + try { + final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir; + for (int userId : userIds) { + if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName, + nativeLibPath, userId) < 0) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Failed linking native library dir (user=" + userId + ")"); + } } + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } } @@ -7051,8 +7101,12 @@ public class PackageManagerService extends IPackageManager.Stub { } if ((scanFlags & SCAN_NO_DEX) == 0) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); + int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instruction sets */, forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, false /* inclDependencies */); + + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); if (result == PackageDexOptimizer.DEX_OPT_FAILED) { throw new PackageManagerException(INSTALL_FAILED_DEXOPT, "scanPackageLI"); } @@ -7141,8 +7195,12 @@ public class PackageManagerService extends IPackageManager.Stub { // so that we do not end up in a confused state while the user is still using the older // version of the application while the new one gets installed. if ((scanFlags & SCAN_REPLACING) != 0) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "killApplication"); + killApplication(pkg.applicationInfo.packageName, pkg.applicationInfo.uid, "replace pkg"); + + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } // Also need to kill any apps that are dependent on the library. @@ -7159,6 +7217,9 @@ public class PackageManagerService extends IPackageManager.Stub { ksms.assertScannedPackageValid(pkg); // writer + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings"); + + boolean createIdmapFailed = false; synchronized (mPackages) { // We don't expect installation to fail beyond this point @@ -7501,8 +7562,7 @@ public class PackageManagerService extends IPackageManager.Stub { map.put(pkg.packageName, pkg); PackageParser.Package orig = mPackages.get(pkg.mOverlayTarget); if (orig != null && !createIdmapForPackagePairLI(orig, pkg)) { - throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, - "scanPackageLI failed to createIdmap"); + createIdmapFailed = true; } } } else if (mOverlays.containsKey(pkg.packageName) && @@ -7512,6 +7572,12 @@ public class PackageManagerService extends IPackageManager.Stub { } } + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + + if (createIdmapFailed) { + throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, + "scanPackageLI failed to createIdmap"); + } return pkg; } @@ -8310,6 +8376,8 @@ public class PackageManagerService extends IPackageManager.Stub { return; } + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "grantPermissions"); + PermissionsState permissionsState = ps.getPermissionsState(); PermissionsState origPermissions = permissionsState; @@ -8536,6 +8604,8 @@ public class PackageManagerService extends IPackageManager.Stub { for (int userId : changedRuntimePermissionUserIds) { mSettings.writeRuntimePermissionsForUserLPr(userId, false); } + + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) { @@ -9528,6 +9598,10 @@ public class PackageManagerService extends IPackageManager.Stub { msg.obj = new InstallParams(origin, null, observer, params.installFlags, installerPackageName, params.volumeUuid, verifParams, user, params.abiOverride, params.grantedRuntimePermissions); + + Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", + System.identityHashCode(msg.obj)); + mHandler.sendMessage(msg); } @@ -10103,7 +10177,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { args.doPreInstall(res.returnCode); synchronized (mInstallLock) { - installPackageLI(args, res); + installPackageTracedLI(args, res); } args.doPostInstall(res.returnCode, res.uid); } @@ -10123,8 +10197,6 @@ public class PackageManagerService extends IPackageManager.Stub { if (mNextInstallToken < 0) mNextInstallToken = 1; token = mNextInstallToken++; - PostInstallData data = new PostInstallData(args, res); - mRunningInstalls.put(token, data); if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token); if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) { @@ -10137,6 +10209,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (bm != null) { if (DEBUG_INSTALL) Log.v(TAG, "token " + token + " to BM for possible restore"); + Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token); try { if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) { bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token); @@ -10148,6 +10221,8 @@ public class PackageManagerService extends IPackageManager.Stub { } catch (Exception e) { Slog.e(TAG, "Exception trying to enqueue restore", e); doRestore = false; + } finally { + Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "restore", token); } } else { Slog.e(TAG, "Backup Manager not found!"); @@ -10159,6 +10234,11 @@ public class PackageManagerService extends IPackageManager.Stub { // No restore possible, or the Backup Manager was mysteriously not // available -- just fire the post-install work request directly. if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token); + + Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "postInstall", token); + + PostInstallData data = new PostInstallData(args, res); + mRunningInstalls.put(token, data); Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0); mHandler.sendMessage(msg); } @@ -10522,7 +10602,6 @@ public class PackageManagerService extends IPackageManager.Stub { final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0; final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0; - PackageInfoLite pkgLite = null; if (onInt && onSd) { @@ -10718,6 +10797,8 @@ public class PackageManagerService extends IPackageManager.Stub { mRequiredVerifierPackage, receivers); if (ret == PackageManager.INSTALL_SUCCEEDED && mRequiredVerifierPackage != null) { + Trace.asyncTraceBegin( + TRACE_TAG_PACKAGE_MANAGER, "pendingVerification", verificationId); /* * Send the intent to the required verification agent, * but only start the verification timeout after the @@ -10984,6 +11065,15 @@ public class PackageManagerService extends IPackageManager.Stub { } int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk"); + try { + return doCopyApk(imcs, temp); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { if (origin.staged) { if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy"); codeFile = origin.file; @@ -11693,12 +11783,15 @@ public class PackageManagerService extends IPackageManager.Stub { private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user, String installerPackageName, String volumeUuid, PackageInstalledInfo res) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage"); + // Remember this for later, in case we need to rollback this install String pkgName = pkg.packageName; if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg); final boolean dataDirExists = Environment .getDataUserPackageDirectory(volumeUuid, UserHandle.USER_OWNER, pkgName).exists(); + synchronized(mPackages) { if (mSettings.mRenamedPackages.containsKey(pkgName)) { // A package with the same name is already installed, though @@ -11719,7 +11812,7 @@ public class PackageManagerService extends IPackageManager.Stub { } try { - PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags, + PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags, System.currentTimeMillis(), user); updateSettingsLI(newPackage, installerPackageName, volumeUuid, null, null, res, user); @@ -11738,6 +11831,8 @@ public class PackageManagerService extends IPackageManager.Stub { } catch (PackageManagerException e) { res.setError("Package couldn't be installed in " + pkg.codePath, e); } + + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } private boolean shouldCheckUpgradeKeySetLP(PackageSetting oldPs, int scanFlags) { @@ -11864,7 +11959,7 @@ public class PackageManagerService extends IPackageManager.Stub { deleteCodeCacheDirsLI(pkg.volumeUuid, pkgName); try { - final PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, + final PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user); updateSettingsLI(newPackage, installerPackageName, volumeUuid, allUsers, perUserInstalled, res, user); @@ -11898,7 +11993,7 @@ public class PackageManagerService extends IPackageManager.Stub { (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0); int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME; try { - scanPackageLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime, null); + scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime, null); } catch (PackageManagerException e) { Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade: " + e.getMessage()); @@ -11980,7 +12075,7 @@ public class PackageManagerService extends IPackageManager.Stub { PackageParser.Package newPackage = null; try { - newPackage = scanPackageLI(pkg, parseFlags, scanFlags, 0, user); + newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags, 0, user); if (newPackage.mExtras != null) { final PackageSetting newPkgSetting = (PackageSetting) newPackage.mExtras; newPkgSetting.firstInstallTime = oldPkgSetting.firstInstallTime; @@ -12013,7 +12108,7 @@ public class PackageManagerService extends IPackageManager.Stub { } // Add back the old system package try { - scanPackageLI(oldPkg, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user); + scanPackageTracedLI(oldPkg, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user); } catch (PackageManagerException e) { Slog.e(TAG, "Failed to restore original package: " + e.getMessage()); } @@ -12034,6 +12129,8 @@ public class PackageManagerService extends IPackageManager.Stub { private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName, String volumeUuid, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res, UserHandle user) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings"); + String pkgName = newPackage.packageName; synchronized (mPackages) { //write settings. the installStatus will be incomplete at this stage. @@ -12044,7 +12141,6 @@ public class PackageManagerService extends IPackageManager.Stub { } if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.codePath); - synchronized (mPackages) { updatePermissionsLPw(newPackage.packageName, newPackage, UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0 @@ -12095,6 +12191,17 @@ public class PackageManagerService extends IPackageManager.Stub { //to update install status mSettings.writeLPr(); } + + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) { + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage"); + installPackageLI(args, res); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } } private void installPackageLI(InstallArgs args, PackageInstalledInfo res) { @@ -12115,6 +12222,7 @@ public class PackageManagerService extends IPackageManager.Stub { res.returnCode = PackageManager.INSTALL_SUCCEEDED; if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile); + // Retrieve PackageSettings and parse package final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) @@ -12123,12 +12231,15 @@ public class PackageManagerService extends IPackageManager.Stub { pp.setSeparateProcesses(mSeparateProcesses); pp.setDisplayMetrics(mMetrics); + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); final PackageParser.Package pkg; try { pkg = pp.parsePackage(tmpPackageFile, parseFlags); } catch (PackageParserException e) { res.setError("Failed parse during installPackageLI", e); return; + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } // Mark that we have an install time CPU ABI override. @@ -12142,12 +12253,15 @@ public class PackageManagerService extends IPackageManager.Stub { } } + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); try { pp.collectCertificates(pkg, parseFlags); pp.collectManifestDigest(pkg); } catch (PackageParserException e) { res.setError("Failed collect during installPackageLI", e); return; + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } /* If the installer passed in a manifest digest, compare it now. */ @@ -12929,7 +13043,7 @@ public class PackageManagerService extends IPackageManager.Stub { final PackageParser.Package newPkg; try { - newPkg = scanPackageLI(disabledPs.codePath, parseFlags, SCAN_NO_PATHS, 0, null); + newPkg = scanPackageTracedLI(disabledPs.codePath, parseFlags, SCAN_NO_PATHS, 0, null); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to restore system package:" + newPs.name + ": " + e.getMessage()); return false; @@ -15444,7 +15558,7 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mInstallLock) { PackageParser.Package pkg = null; try { - pkg = scanPackageLI(new File(codePath), parseFlags, 0, 0, null); + pkg = scanPackageTracedLI(new File(codePath), parseFlags, 0, 0, null); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to scan " + codePath + ": " + e.getMessage()); } @@ -15590,7 +15704,7 @@ public class PackageManagerService extends IPackageManager.Stub { for (PackageSetting ps : packages) { final PackageParser.Package pkg; try { - pkg = scanPackageLI(ps.codePath, parseFlags, SCAN_INITIAL, 0L, null); + pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0L, null); loaded.add(pkg.applicationInfo); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage()); diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index b920f97ac839..7ebb7f817377 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -2269,7 +2269,7 @@ public final class PowerManagerService extends SystemService public void run() { synchronized (this) { if (shutdown) { - ShutdownThread.shutdown(mContext, confirm); + ShutdownThread.shutdown(mContext, reason, confirm); } else { ShutdownThread.reboot(mContext, reason, confirm); } @@ -2546,9 +2546,14 @@ public final class PowerManagerService extends SystemService /** * Low-level function turn the device off immediately, without trying * to be clean. Most people should use {@link ShutdownThread} for a clean shutdown. + * + * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null. */ - public static void lowLevelShutdown() { - SystemProperties.set("sys.powerctl", "shutdown"); + public static void lowLevelShutdown(String reason) { + if (reason == null) { + reason = ""; + } + SystemProperties.set("sys.powerctl", "shutdown," + reason); } /** @@ -3277,12 +3282,12 @@ public final class PowerManagerService extends SystemService * @param wait If true, this call waits for the shutdown to complete and does not return. */ @Override // Binder call - public void shutdown(boolean confirm, boolean wait) { + public void shutdown(boolean confirm, String reason, boolean wait) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null); final long ident = Binder.clearCallingIdentity(); try { - shutdownOrRebootInternal(true, confirm, null, wait); + shutdownOrRebootInternal(true, confirm, reason, wait); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java index dd8648d60d8f..ac6a28e91ca8 100644 --- a/services/core/java/com/android/server/power/ShutdownThread.java +++ b/services/core/java/com/android/server/power/ShutdownThread.java @@ -88,7 +88,7 @@ public final class ShutdownThread extends Thread { private static boolean mReboot; private static boolean mRebootSafeMode; private static boolean mRebootUpdate; - private static String mRebootReason; + private static String mReason; // Provides shutdown assurance in case the system_server is killed public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested"; @@ -124,11 +124,13 @@ public final class ShutdownThread extends Thread { * is shown. * * @param context Context used to display the shutdown progress dialog. + * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null. * @param confirm true if user confirmation is needed before shutting down. */ - public static void shutdown(final Context context, boolean confirm) { + public static void shutdown(final Context context, String reason, boolean confirm) { mReboot = false; mRebootSafeMode = false; + mReason = reason; shutdownInner(context, confirm); } @@ -212,7 +214,7 @@ public final class ShutdownThread extends Thread { mReboot = true; mRebootSafeMode = false; mRebootUpdate = false; - mRebootReason = reason; + mReason = reason; shutdownInner(context, confirm); } @@ -232,7 +234,7 @@ public final class ShutdownThread extends Thread { mReboot = true; mRebootSafeMode = true; mRebootUpdate = false; - mRebootReason = null; + mReason = null; shutdownInner(context, confirm); } @@ -249,18 +251,18 @@ public final class ShutdownThread extends Thread { ProgressDialog pd = new ProgressDialog(context); // Path 1: Reboot to recovery and install the update - // Condition: mRebootReason == REBOOT_RECOVERY and mRebootUpdate == True + // Condition: mReason == REBOOT_RECOVERY and mRebootUpdate == True // (mRebootUpdate is set by checking if /cache/recovery/uncrypt_file exists.) // UI: progress bar // // Path 2: Reboot to recovery for factory reset - // Condition: mRebootReason == REBOOT_RECOVERY + // Condition: mReason == REBOOT_RECOVERY // UI: spinning circle only (no progress bar) // // Path 3: Regular reboot / shutdown // Condition: Otherwise // UI: spinning circle only (no progress bar) - if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) { + if (PowerManager.REBOOT_RECOVERY.equals(mReason)) { mRebootUpdate = new File(UNCRYPT_PACKAGE_FILE).exists(); if (mRebootUpdate) { pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title)); @@ -349,7 +351,7 @@ public final class ShutdownThread extends Thread { * the beginning of the SystemServer startup. */ { - String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : ""); + String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : ""); SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason); } @@ -473,7 +475,7 @@ public final class ShutdownThread extends Thread { uncrypt(); } - rebootOrShutdown(mContext, mReboot, mRebootReason); + rebootOrShutdown(mContext, mReboot, mReason); } private void setRebootProgress(final int progress, final CharSequence message) { @@ -616,13 +618,14 @@ public final class ShutdownThread extends Thread { * * @param context Context used to vibrate or null without vibration * @param reboot true to reboot or false to shutdown - * @param reason reason for reboot + * @param reason reason for reboot/shutdown */ public static void rebootOrShutdown(final Context context, boolean reboot, String reason) { if (reboot) { Log.i(TAG, "Rebooting, reason: " + reason); PowerManagerService.lowLevelReboot(reason); Log.e(TAG, "Reboot failed, will attempt shutdown instead"); + reason = null; } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) { // vibrate before shutting down Vibrator vibrator = new SystemVibrator(context); @@ -642,7 +645,7 @@ public final class ShutdownThread extends Thread { // Shutdown power Log.i(TAG, "Performing low-level shutdown..."); - PowerManagerService.lowLevelShutdown(); + PowerManagerService.lowLevelShutdown(reason); } private void uncrypt() { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index af21eb3609a8..20e24bbd628c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -327,7 +327,7 @@ public class WindowManagerService extends IWindowManager.Stub * Users that are profiles of the current user. These are also allowed to show windows * on the current user. */ - int[] mCurrentProfileIds = new int[] {UserHandle.USER_OWNER}; + int[] mCurrentProfileIds = new int[] {}; final Context mContext; @@ -5675,7 +5675,7 @@ public class WindowManagerService extends IWindowManager.Stub // Called by window manager policy. Not exposed externally. @Override public void shutdown(boolean confirm) { - ShutdownThread.shutdown(mContext, confirm); + ShutdownThread.shutdown(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm); } // Called by window manager policy. Not exposed externally. diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java index 1105c7b0fb19..a503e50407ed 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java @@ -673,7 +673,7 @@ public final class Matrix_Delegate { return; } - System.arraycopy(d.mValues, 0, d.mValues, 0, MATRIX_SIZE); + System.arraycopy(d.mValues, 0, values, 0, MATRIX_SIZE); } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java index 34d09859e2b4..3c9a062719e2 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java @@ -36,7 +36,6 @@ import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; -import java.util.ArrayList; /** * Delegate implementing the native methods of android.graphics.Path @@ -504,13 +503,13 @@ public final class Path_Delegate { switch (type) { case PathIterator.SEG_MOVETO: case PathIterator.SEG_LINETO: - store(coords, tmp, 1, isFirstPoint); + store(tmp, coords, 1, isFirstPoint); break; case PathIterator.SEG_QUADTO: - store(coords, tmp, 2, isFirstPoint); + store(tmp, coords, 2, isFirstPoint); break; case PathIterator.SEG_CUBICTO: - store(coords, tmp, 3, isFirstPoint); + store(tmp, coords, 3, isFirstPoint); break; case PathIterator.SEG_CLOSE: // No points returned. @@ -528,14 +527,14 @@ public final class Path_Delegate { private static void store(float[] src, float[] dst, int count, boolean isFirst) { if (isFirst) { - dst[0] = 0; - dst[1] = src[0]; - dst[2] = src[1]; + dst[0] = 0; // fraction + dst[1] = src[0]; // abscissa + dst[2] = src[1]; // ordinate } if (count > 1 || !isFirst) { dst[3] = 1; - dst[4] = src[2 * count]; - dst[5] = src[2 * count + 1]; + dst[4] = src[2 * count - 2]; + dst[5] = src[2 * count - 1]; } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java index 895f9c9eaeac..a410c53eb22b 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java @@ -86,7 +86,7 @@ public class BridgePowerManager implements IPowerManager { } @Override - public void shutdown(boolean confirm, boolean wait) { + public void shutdown(boolean confirm, String reason, boolean wait) { // pass for now. } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java index 8c7ea8a11085..c72c97930b26 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java @@ -83,7 +83,7 @@ class Layout extends RelativeLayout { // Theme attributes used for configuring appearance of the system decor. private static final String ATTR_WINDOW_FLOATING = "windowIsFloating"; private static final String ATTR_WINDOW_BACKGROUND = "windowBackground"; - private static final String ATTR_WINDOW_FULL_SCREEN = "windowFullScreen"; + private static final String ATTR_WINDOW_FULL_SCREEN = "windowFullscreen"; private static final String ATTR_NAV_BAR_HEIGHT = "navigation_bar_height"; private static final String ATTR_NAV_BAR_WIDTH = "navigation_bar_width"; private static final String ATTR_STATUS_BAR_HEIGHT = "status_bar_height"; @@ -329,9 +329,12 @@ class Layout extends RelativeLayout { mWindowIsFloating = getBooleanThemeValue(mResources, ATTR_WINDOW_FLOATING, true, true); findBackground(); - findStatusBar(); - findActionBar(); - findNavBar(); + + if (!mParams.isForceNoDecor()) { + findStatusBar(); + findActionBar(); + findNavBar(); + } } private void findBackground() { diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/array_check.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/array_check.png Binary files differindex 9a135684f33e..336f9d8b4798 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/array_check.png +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/array_check.png diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png Binary files differindex 92eb3e10148b..0c1621544870 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java index ee448caf97fe..9ebeebd49c82 100644 --- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java @@ -329,8 +329,8 @@ public class Main { .setNavigation(Navigation.NONAV); SessionParams params = getSessionParams(parser, customConfigGenerator, - layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", RenderingMode.V_SCROLL, - 22); + layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false, + RenderingMode.V_SCROLL, 22); renderAndVerify(params, "expand_vert_layout.png"); @@ -342,8 +342,8 @@ public class Main { parser = new LayoutPullParser(APP_TEST_RES + "/layout/" + "expand_horz_layout.xml"); params = getSessionParams(parser, customConfigGenerator, - layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", RenderingMode - .H_SCROLL, 22); + layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false, + RenderingMode.H_SCROLL, 22); renderAndVerify(params, "expand_horz_layout.png"); } @@ -390,7 +390,7 @@ public class Main { // TODO: Set up action bar handler properly to test menu rendering. // Create session params. SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5, - layoutLibCallback, "Theme.Material.Light.DarkActionBar", RenderingMode.NORMAL, 22); + layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22); renderAndVerify(params, goldenFileName); } @@ -399,12 +399,12 @@ public class Main { */ private SessionParams getSessionParams(LayoutPullParser layoutParser, ConfigGenerator configGenerator, LayoutLibTestCallback layoutLibCallback, - String themeName, RenderingMode renderingMode, int targetSdk) { + String themeName, boolean isProjectTheme, RenderingMode renderingMode, int targetSdk) { FolderConfiguration config = configGenerator.getFolderConfig(); ResourceResolver resourceResolver = ResourceResolver.create(sProjectResources.getConfiguredResources(config), sFrameworkRepo.getConfiguredResources(config), - themeName, false); + themeName, isProjectTheme); return new SessionParams( layoutParser, |