diff options
25 files changed, 332 insertions, 87 deletions
diff --git a/api/current.txt b/api/current.txt index 7907e563bae3..785d7a5bc62b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -26343,6 +26343,7 @@ package android.view { method public float getY(); method public boolean hasFocus(); method public boolean hasFocusable(); + method public boolean hasLayout(); method public boolean hasOnClickListeners(); method public boolean hasOverlappingRendering(); method public boolean hasTransientState(); @@ -26355,6 +26356,7 @@ package android.view { method public void invalidate(); method public void invalidateDrawable(android.graphics.drawable.Drawable); method public boolean isActivated(); + method public boolean isAttachedToWindow(); method public boolean isClickable(); method public boolean isDirty(); method public boolean isDrawingCacheEnabled(); @@ -27970,10 +27972,10 @@ package android.view.animation { method public void setAlpha(float); method public void setTransformationType(int); method public java.lang.String toShortString(); - field public static int TYPE_ALPHA; - field public static int TYPE_BOTH; - field public static int TYPE_IDENTITY; - field public static int TYPE_MATRIX; + field public static final int TYPE_ALPHA = 1; // 0x1 + field public static final int TYPE_BOTH = 3; // 0x3 + field public static final int TYPE_IDENTITY = 0; // 0x0 + field public static final int TYPE_MATRIX = 2; // 0x2 field protected float mAlpha; field protected android.graphics.Matrix mMatrix; field protected int mTransformationType; @@ -28512,6 +28514,7 @@ package android.view.transition { public class TransitionManager { ctor public TransitionManager(); + method public static void beginDelayedTransition(android.view.ViewGroup, android.view.transition.Transition); method public android.view.transition.Transition getDefaultTransition(); method public static void go(android.view.transition.Scene); method public static void go(android.view.transition.Scene, android.view.transition.Transition); diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 634fa30c1c40..cda2c5f3b7c5 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -306,6 +306,7 @@ public class Dialog implements DialogInterface, Window.Callback, * method to do cleanup when the dialog is dismissed, instead implement * that in {@link #onStop}. */ + @Override public void dismiss() { if (Looper.myLooper() == mHandler.getLooper()) { dismissDialog(); @@ -325,7 +326,7 @@ public class Dialog implements DialogInterface, Window.Callback, } try { - mWindowManager.removeView(mDecor); + mWindowManager.removeViewImmediate(mDecor); } finally { if (mActionMode != null) { mActionMode.finish(); @@ -334,7 +335,7 @@ public class Dialog implements DialogInterface, Window.Callback, mWindow.closeAllPanels(); onStop(); mShowing = false; - + sendDismissMessage(); } } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 7a0f3769e5da..bffbb519a89a 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1696,6 +1696,9 @@ public class Notification implements Parcelable extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, mProgressIndeterminate); extras.putBoolean(EXTRA_SHOW_CHRONOMETER, mUseChronometer); extras.putBoolean(EXTRA_SHOW_WHEN, mShowWhen); + if (mLargeIcon != null) { + extras.putParcelable(EXTRA_LARGE_ICON, mLargeIcon); + } } /** diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 55a8f747b5b2..e860dcaab2e4 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2195,6 +2195,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ static final int PFLAG3_VIEW_IS_ANIMATING_ALPHA = 0x2; + /** + * Flag indicating that the view has been through at least one layout since it + * was last attached to a window. + */ + static final int PFLAG3_HAS_LAYOUT = 0x4; + /* End of masks for mPrivateFlags3 */ @@ -6138,6 +6144,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Returns true if this view is currently attached to a window. + */ + public boolean isAttachedToWindow() { + return mAttachInfo != null; + } + + /** + * Returns true if this view has been through at least one layout since it + * was last attached to or detached from a window. + */ + public boolean hasLayout() { + return (mPrivateFlags3 & PFLAG3_HAS_LAYOUT) == PFLAG3_HAS_LAYOUT; + } + + /** * If this view doesn't do any drawing on its own, set this flag to * allow further optimizations. By default, this flag is not set on * View, but could be set on some View subclasses such as ViewGroup. @@ -7024,7 +7045,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @hide */ - private void notifySubtreeAccessibilityStateChangedIfNeeded() { + public void notifySubtreeAccessibilityStateChangedIfNeeded() { if (!AccessibilityManager.getInstance(mContext).isEnabled()) { return; } @@ -11779,6 +11800,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags &= ~PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH; } + mPrivateFlags3 &= ~PFLAG3_HAS_LAYOUT; + jumpDrawablesToCurrentState(); clearAccessibilityFocus(); @@ -12065,6 +12088,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ protected void onDetachedFromWindow() { mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT; + mPrivateFlags3 &= ~PFLAG3_HAS_LAYOUT; removeUnsetPressCallback(); removeLongPressCallback(); @@ -14371,6 +14395,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } mPrivateFlags &= ~PFLAG_FORCE_LAYOUT; + mPrivateFlags3 |= PFLAG3_HAS_LAYOUT; } /** @@ -18867,6 +18892,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final long minEventIntevalMillis = ViewConfiguration.getSendRecurringAccessibilityEventsInterval(); if (timeSinceLastMillis >= minEventIntevalMillis) { + removeCallbacks(this); run(); } else { postDelayed(this, minEventIntevalMillis - timeSinceLastMillis); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index caed4a27d495..29b2e4bea80c 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -3595,7 +3595,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } if (child.isImportantForAccessibility() && child.getVisibility() != View.GONE) { - childAccessibilityStateChanged(child); + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -3838,7 +3838,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager onViewRemoved(view); if (view.isImportantForAccessibility() && view.getVisibility() != View.GONE) { - childAccessibilityStateChanged(view); + notifySubtreeAccessibilityStateChangedIfNeeded(); } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index a5f92de09294..60d8a75c749c 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2787,6 +2787,7 @@ public final class ViewRootImpl implements ViewParent, setAccessibilityFocus(null, null); + mView.assignParent(null); mView = null; mAttachInfo.mRootView = null; mAttachInfo.mSurface = null; @@ -6397,12 +6398,10 @@ public final class ViewRootImpl implements ViewParent, public void run() { mLastEventTimeMillis = SystemClock.uptimeMillis(); - if (AccessibilityManager.getInstance(mContext).isEnabled()) { - AccessibilityEvent event = AccessibilityEvent.obtain(); - event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); - event.setContentChangeType(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE); - mSource.sendAccessibilityEventUnchecked(event); - } + AccessibilityEvent event = AccessibilityEvent.obtain(); + event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); + event.setContentChangeType(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE); + mSource.sendAccessibilityEventUnchecked(event); mSource.resetSubtreeAccessibilityStateChanged(); mSource = null; } @@ -6417,6 +6416,7 @@ public final class ViewRootImpl implements ViewParent, final long minEventIntevalMillis = ViewConfiguration.getSendRecurringAccessibilityEventsInterval(); if (timeSinceLastMillis >= minEventIntevalMillis) { + mSource.removeCallbacks(this); run(); } else { mSource.postDelayed(this, minEventIntevalMillis - timeSinceLastMillis); diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 6bcf863aa6e6..f7b85cc7436c 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -335,15 +335,13 @@ public final class WindowManagerGlobal { } } root.die(immediate); + if (view != null) { + view.assignParent(null); + } } void doRemoveView(ViewRootImpl root) { synchronized (mLock) { - final View view = root.getView(); - if (view != null) { - view.assignParent(null); - } - final int index = mRoots.indexOf(root); if (index >= 0) { mRoots.remove(index); diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java index e8c1d231dbfb..890909bbcaf9 100644 --- a/core/java/android/view/animation/Transformation.java +++ b/core/java/android/view/animation/Transformation.java @@ -29,19 +29,19 @@ public class Transformation { /** * Indicates a transformation that has no effect (alpha = 1 and identity matrix.) */ - public static int TYPE_IDENTITY = 0x0; + public static final int TYPE_IDENTITY = 0x0; /** * Indicates a transformation that applies an alpha only (uses an identity matrix.) */ - public static int TYPE_ALPHA = 0x1; + public static final int TYPE_ALPHA = 0x1; /** * Indicates a transformation that applies a matrix only (alpha = 1.) */ - public static int TYPE_MATRIX = 0x2; + public static final int TYPE_MATRIX = 0x2; /** * Indicates a transformation that applies an alpha and a matrix. */ - public static int TYPE_BOTH = TYPE_ALPHA | TYPE_MATRIX; + public static final int TYPE_BOTH = TYPE_ALPHA | TYPE_MATRIX; protected Matrix mMatrix; protected float mAlpha; diff --git a/core/java/android/view/transition/TransitionManager.java b/core/java/android/view/transition/TransitionManager.java index 4971a92466b3..8088f6b2c23d 100644 --- a/core/java/android/view/transition/TransitionManager.java +++ b/core/java/android/view/transition/TransitionManager.java @@ -19,6 +19,8 @@ import android.util.ArrayMap; import android.view.ViewGroup; import android.view.ViewTreeObserver; +import java.util.ArrayList; + /** * This class manages the set of transitions that fire when there is a * change of {@link Scene}. To use the manager, add scenes along with @@ -41,8 +43,10 @@ public class TransitionManager { new ArrayMap<Scene, ArrayMap<Scene, Transition>>(); static ArrayMap<ViewGroup, Transition> sRunningTransitions = new ArrayMap<ViewGroup, Transition>(); + private static ArrayList<ViewGroup> sPendingTransitions = new ArrayList<ViewGroup>(); + - /** + /** * Sets the transition to be used for any scene change for which no * other transition is explicitly set. The initial value is * an {@link AutoTransition} instance. @@ -142,24 +146,15 @@ public class TransitionManager { final ViewGroup sceneRoot = scene.getSceneRoot(); - Transition runningTransition = sRunningTransitions.get(sceneRoot); - if (runningTransition != null) { - runningTransition.cancelTransition(); - } - - // Capture current values - if (transition != null) { - transition.captureValues(sceneRoot, true); - } - - // Notify previous scene that it is being exited - Scene previousScene = sceneRoot.getCurrentScene(); - if (previousScene != null) { - previousScene.exit(); - } + sceneChangeSetup(sceneRoot, transition); scene.enter(); + sceneChangeRunTransition(sceneRoot, transition); + } + + private static void sceneChangeRunTransition(final ViewGroup sceneRoot, + final Transition transition) { if (transition != null) { final ViewTreeObserver observer = sceneRoot.getViewTreeObserver(); observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @@ -181,6 +176,25 @@ public class TransitionManager { } } + private static void sceneChangeSetup(ViewGroup sceneRoot, Transition transition) { + + Transition runningTransition = sRunningTransitions.get(sceneRoot); + if (runningTransition != null) { + runningTransition.cancelTransition(); + } + + // Capture current values + if (transition != null) { + transition.captureValues(sceneRoot, true); + } + + // Notify previous scene that it is being exited + Scene previousScene = sceneRoot.getCurrentScene(); + if (previousScene != null) { + previousScene.exit(); + } + } + /** * Change to the given scene, using the * appropriate transition for this particular scene change @@ -272,4 +286,47 @@ public class TransitionManager { scene.setEnterAction(action); changeScene(scene, transition); } + + /** + * Static utility method to animate to a new scene defined by all changes within + * the given scene root between calling this method and the next rendering frame. + * Calling this method causes TransitionManager to capture current values in the + * scene root and then post a request to run a transition on the next frame. + * At that time, the new values in the scene root will be captured and changes + * will be animated. There is no need to create a Scene; it is implied by + * changes which take place between calling this method and the next frame when + * the transition begins. + * + * <p>Calling this method several times before the next frame (for example, if + * unrelated code also wants to make dynamic changes and run a transition on + * the same scene root), only the first call will trigger capturing values + * and exiting the current scene. Subsequent calls to the method with the + * same scene root during the same frame will be ignored.</p> + * + * <p>Passing in <code>null</code> for the transition parameter will + * cause the TransitionManager to use its default transition.</p> + * + * @param sceneRoot The root of the View hierarchy to run the transition on. + * @param transition The transition to use for this change. A + * value of null causes the TransitionManager to use the default transition. + */ + public static void beginDelayedTransition(final ViewGroup sceneRoot, Transition transition) { + + if (!sPendingTransitions.contains(sceneRoot)) { + sPendingTransitions.add(sceneRoot); + if (transition == null) { + transition = sDefaultTransition; + } + final Transition finalTransition = transition; + sceneChangeSetup(sceneRoot, transition); + sceneRoot.setCurrentScene(null); + sceneRoot.postOnAnimation(new Runnable() { + @Override + public void run() { + sPendingTransitions.remove(sceneRoot); + sceneChangeRunTransition(sceneRoot, finalTransition); + } + }); + } + } } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index ddc8d8266bf4..1991af1d65a9 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -6519,7 +6519,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te int viewType = lp.viewType; final boolean scrapHasTransientState = scrap.hasTransientState(); if (!shouldRecycleViewType(viewType) || scrapHasTransientState) { - if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER || scrapHasTransientState) { + if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER && scrapHasTransientState) { if (mSkippedScrap == null) { mSkippedScrap = new ArrayList<View>(); } @@ -6593,7 +6593,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te final boolean scrapHasTransientState = victim.hasTransientState(); if (!shouldRecycleViewType(whichScrap) || scrapHasTransientState) { // Do not move views that should be ignored - if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER || + if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER && scrapHasTransientState) { removeDetachedView(victim, false); } diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index 4a2df58c5415..31ab0afa4334 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -1033,7 +1033,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { checkSelectionChanged(); } - childAccessibilityStateChanged(this); + notifySubtreeAccessibilityStateChangedIfNeeded(); } void checkSelectionChanged() { diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 70d65eb124bf..bd40c643e30b 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -944,11 +944,11 @@ <item quantity="other" msgid="3903706804349556379">"vor <xliff:g id="COUNT">%d</xliff:g> Sekunden"</item> </plurals> <plurals name="num_minutes_ago"> - <item quantity="one" msgid="3306787433088810191">"Vor 1 Minute"</item> + <item quantity="one" msgid="3306787433088810191">"vor 1 Minute"</item> <item quantity="other" msgid="2176942008915455116">"vor <xliff:g id="COUNT">%d</xliff:g> Minuten"</item> </plurals> <plurals name="num_hours_ago"> - <item quantity="one" msgid="9150797944610821849">"Vor 1 Stunde"</item> + <item quantity="one" msgid="9150797944610821849">"vor 1 Stunde"</item> <item quantity="other" msgid="2467273239587587569">"vor <xliff:g id="COUNT">%d</xliff:g> Stunden"</item> </plurals> <plurals name="last_num_days"> @@ -957,7 +957,7 @@ <string name="last_month" msgid="3959346739979055432">"Letzter Monat"</string> <string name="older" msgid="5211975022815554840">"Älter"</string> <plurals name="num_days_ago"> - <item quantity="one" msgid="861358534398115820">"Gestern"</item> + <item quantity="one" msgid="861358534398115820">"gestern"</item> <item quantity="other" msgid="2479586466153314633">"vor <xliff:g id="COUNT">%d</xliff:g> Tagen"</item> </plurals> <plurals name="in_num_seconds"> @@ -985,11 +985,11 @@ <item quantity="other" msgid="851164968597150710">"vor <xliff:g id="COUNT">%d</xliff:g> Minuten"</item> </plurals> <plurals name="abbrev_num_hours_ago"> - <item quantity="one" msgid="4796212039724722116">"Vor 1 Stunde"</item> + <item quantity="one" msgid="4796212039724722116">"vor 1 Stunde"</item> <item quantity="other" msgid="6889970745748538901">"vor <xliff:g id="COUNT">%d</xliff:g> Stunden"</item> </plurals> <plurals name="abbrev_num_days_ago"> - <item quantity="one" msgid="8463161711492680309">"Gestern"</item> + <item quantity="one" msgid="8463161711492680309">"gestern"</item> <item quantity="other" msgid="3453342639616481191">"vor <xliff:g id="COUNT">%d</xliff:g> Tagen"</item> </plurals> <plurals name="abbrev_in_num_seconds"> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 3a24738d8f21..c388643bb27f 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -607,7 +607,7 @@ <string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"δοκιμή πρόσβασης σε προστατευμένο χώρο αποθήκευσης"</string> <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Επιτρέπει USB για άλλες συσκ."</string> <string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Επιτρέπει στην εφαρμογή τη δοκιμή μια άδειας για την κάρτα SD που θα διατίθεται σε μελλοντικές συσκευές."</string> - <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"τροπ. ή διαγρ. περιεχ. αποθ. χώρ. USB"</string> + <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"τροπ. ή διαγρ. του USB"</string> <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"τροποποίηση ή διαγραφή των περιεχομένων της κάρτας SD"</string> <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Επιτρέπει στην εφαρμογή την εγγραφή στον αποθηκευτικό χώρο USB."</string> <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Επιτρέπει στην εφαρμογή την εγγραφή στην κάρτα SD."</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index cb580f25c67b..f5ba0fb8837e 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -607,7 +607,7 @@ <string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"testar o acesso ao armazenamento protegido"</string> <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Permite que o aplicativo teste uma permissão para o armazenamento USB que estará disponível em dispositivos futuros."</string> <string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Permite que o aplicativo teste uma permissão para o cartão SD que estará disponível em dispositivos futuros."</string> - <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modif ou excl cont. armaz USB"</string> + <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modificar ou excluir conteúdo do armazenamento USB"</string> <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modificar ou excluir o conteúdo do cartão SD"</string> <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Permite gravar no armaz. USB."</string> <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite que o aplicativo grave em seu cartão SD."</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index a7f58b28deec..d72b1a78b66e 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -1498,7 +1498,7 @@ <string name="user_switched" msgid="3768006783166984410">"Người dùng hiện tại <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Chủ sở hữu"</string> <string name="error_message_title" msgid="4510373083082500195">"Lỗi"</string> - <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Ứng dụng này không hỗ trợ tài khoản cho các tiểu sử bị hạn chế"</string> + <string name="app_no_restricted_accounts" msgid="5739463249673727736">"Ứng dụng này không hỗ trợ tài khoản đối với các tiểu sử bị hạn chế"</string> <string name="app_not_found" msgid="3429141853498927379">"Không tìm thấy ứng dụng nào để xử lý tác vụ này"</string> <string name="revoke" msgid="5404479185228271586">"Thu hồi"</string> </resources> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 90c7c1220c9f..499b3aceb51e 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -621,7 +621,7 @@ <string name="permdesc_cache_filesystem" msgid="5578967642265550955">"允許應用程式讀取及寫入快取檔案系統。"</string> <string name="permlab_use_sip" msgid="5986952362795870502">"撥打/接聽網路電話"</string> <string name="permdesc_use_sip" msgid="4717632000062674294">"允許應用程式使用 SIP 服務撥打/接聽網路電話。"</string> - <string name="permlab_readNetworkUsageHistory" msgid="7862593283611493232">"讀取網路用量記錄"</string> + <string name="permlab_readNetworkUsageHistory" msgid="7862593283611493232">"讀取網路用量紀錄"</string> <string name="permdesc_readNetworkUsageHistory" msgid="7689060749819126472">"允許應用程式讀取特定網路和應用程式的網路使用記錄。"</string> <string name="permlab_manageNetworkPolicy" msgid="2562053592339859990">"管理網路政策"</string> <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"允許應用程式管理網路政策並定義應用程式專用規則。"</string> diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index ad23b667c67e..83739f55e000 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -64,6 +64,7 @@ import com.android.internal.app.IMediaContainerService; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.NativeDaemonConnector.Command; +import com.android.server.NativeDaemonConnector.SensitiveArg; import com.android.server.am.ActivityManagerService; import com.android.server.pm.PackageManagerService; import com.android.server.pm.UserManagerService; @@ -1641,8 +1642,8 @@ class MountService extends IMountService.Stub int rc = StorageResultCode.OperationSucceeded; try { - mConnector.execute("asec", "create", id, sizeMb, fstype, key, ownerUid, - external ? "1" : "0"); + mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key), + ownerUid, external ? "1" : "0"); } catch (NativeDaemonConnectorException e) { rc = StorageResultCode.OperationFailedInternalError; } @@ -1742,7 +1743,7 @@ class MountService extends IMountService.Stub int rc = StorageResultCode.OperationSucceeded; try { - mConnector.execute("asec", "mount", id, key, ownerUid); + mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid); } catch (NativeDaemonConnectorException e) { int code = e.getCode(); if (code != VoldResponseCode.OpFailedStorageBusy) { @@ -2018,7 +2019,7 @@ class MountService extends IMountService.Stub final NativeDaemonEvent event; try { - event = mConnector.execute("cryptfs", "checkpw", password); + event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(password)); final int code = Integer.parseInt(event.getMessage()); if (code == 0) { @@ -2057,7 +2058,7 @@ class MountService extends IMountService.Stub } try { - mConnector.execute("cryptfs", "enablecrypto", "inplace", password); + mConnector.execute("cryptfs", "enablecrypto", "inplace", new SensitiveArg(password)); } catch (NativeDaemonConnectorException e) { // Encryption failed return e.getCode(); @@ -2082,7 +2083,7 @@ class MountService extends IMountService.Stub final NativeDaemonEvent event; try { - event = mConnector.execute("cryptfs", "changepw", password); + event = mConnector.execute("cryptfs", "changepw", new SensitiveArg(password)); return Integer.parseInt(event.getMessage()); } catch (NativeDaemonConnectorException e) { // Encryption failed @@ -2115,7 +2116,7 @@ class MountService extends IMountService.Stub final NativeDaemonEvent event; try { - event = mConnector.execute("cryptfs", "verifypw", password); + event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(password)); Slog.i(TAG, "cryptfs verifypw => " + event.getMessage()); return Integer.parseInt(event.getMessage()); } catch (NativeDaemonConnectorException e) { @@ -2481,8 +2482,8 @@ class MountService extends IMountService.Stub int rc = StorageResultCode.OperationSucceeded; try { - mConnector.execute( - "obb", "mount", mObbState.voldPath, hashedKey, mObbState.ownerGid); + mConnector.execute("obb", "mount", mObbState.voldPath, new SensitiveArg(hashedKey), + mObbState.ownerGid); } catch (NativeDaemonConnectorException e) { int code = e.getCode(); if (code != VoldResponseCode.OpFailedStorageBusy) { diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java index abcd8eeb35e2..8a8e38d41456 100644 --- a/services/java/com/android/server/NativeDaemonConnector.java +++ b/services/java/com/android/server/NativeDaemonConnector.java @@ -201,9 +201,28 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo } /** + * Wrapper around argument that indicates it's sensitive and shouldn't be + * logged. + */ + public static class SensitiveArg { + private final Object mArg; + + public SensitiveArg(Object arg) { + mArg = arg; + } + + @Override + public String toString() { + return String.valueOf(mArg); + } + } + + /** * Make command for daemon, escaping arguments as needed. */ - private void makeCommand(StringBuilder builder, String cmd, Object... args) { + @VisibleForTesting + static void makeCommand(StringBuilder rawBuilder, StringBuilder logBuilder, int sequenceNumber, + String cmd, Object... args) { if (cmd.indexOf('\0') >= 0) { throw new IllegalArgumentException("Unexpected command: " + cmd); } @@ -211,16 +230,26 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo throw new IllegalArgumentException("Arguments must be separate from command"); } - builder.append(cmd); + rawBuilder.append(sequenceNumber).append(' ').append(cmd); + logBuilder.append(sequenceNumber).append(' ').append(cmd); for (Object arg : args) { final String argString = String.valueOf(arg); if (argString.indexOf('\0') >= 0) { throw new IllegalArgumentException("Unexpected argument: " + arg); } - builder.append(' '); - appendEscaped(builder, argString); + rawBuilder.append(' '); + logBuilder.append(' '); + + appendEscaped(rawBuilder, argString); + if (arg instanceof SensitiveArg) { + logBuilder.append("[scrubbed]"); + } else { + appendEscaped(logBuilder, argString); + } } + + rawBuilder.append('\0'); } /** @@ -300,27 +329,27 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo */ public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args) throws NativeDaemonConnectorException { + final long startTime = SystemClock.elapsedRealtime(); + final ArrayList<NativeDaemonEvent> events = Lists.newArrayList(); + final StringBuilder rawBuilder = new StringBuilder(); + final StringBuilder logBuilder = new StringBuilder(); final int sequenceNumber = mSequenceNumber.incrementAndGet(); - final StringBuilder cmdBuilder = - new StringBuilder(Integer.toString(sequenceNumber)).append(' '); - final long startTime = SystemClock.elapsedRealtime(); - makeCommand(cmdBuilder, cmd, args); + makeCommand(rawBuilder, logBuilder, sequenceNumber, cmd, args); - final String logCmd = cmdBuilder.toString(); /* includes cmdNum, cmd, args */ - log("SND -> {" + logCmd + "}"); + final String rawCmd = rawBuilder.toString(); + final String logCmd = logBuilder.toString(); - cmdBuilder.append('\0'); - final String sentCmd = cmdBuilder.toString(); /* logCmd + \0 */ + log("SND -> {" + logCmd + "}"); synchronized (mDaemonLock) { if (mOutputStream == null) { throw new NativeDaemonConnectorException("missing output stream"); } else { try { - mOutputStream.write(sentCmd.getBytes(Charsets.UTF_8)); + mOutputStream.write(rawCmd.getBytes(Charsets.UTF_8)); } catch (IOException e) { throw new NativeDaemonConnectorException("problem sending command", e); } @@ -329,7 +358,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo NativeDaemonEvent event = null; do { - event = mResponseQueue.remove(sequenceNumber, timeout, sentCmd); + event = mResponseQueue.remove(sequenceNumber, timeout, logCmd); if (event == null) { loge("timed-out waiting for response to " + logCmd); throw new NativeDaemonFailureException(logCmd, event); @@ -444,10 +473,11 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo private static class ResponseQueue { private static class PendingCmd { - public int cmdNum; + public final int cmdNum; + public final String logCmd; + public BlockingQueue<NativeDaemonEvent> responses = new ArrayBlockingQueue<NativeDaemonEvent>(10); - public String request; // The availableResponseCount member is used to track when we can remove this // instance from the ResponseQueue. @@ -465,7 +495,11 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo // hold references to this instance already so it can be removed from // mPendingCmds queue. public int availableResponseCount; - public PendingCmd(int c, String r) {cmdNum = c; request = r;} + + public PendingCmd(int cmdNum, String logCmd) { + this.cmdNum = cmdNum; + this.logCmd = logCmd; + } } private final LinkedList<PendingCmd> mPendingCmds; @@ -494,7 +528,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo // let any waiter timeout waiting for this PendingCmd pendingCmd = mPendingCmds.remove(); Slog.e("NativeDaemonConnector.ResponseQueue", - "Removing request: " + pendingCmd.request + " (" + + "Removing request: " + pendingCmd.logCmd + " (" + pendingCmd.cmdNum + ")"); } found = new PendingCmd(cmdNum, null); @@ -512,7 +546,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo // note that the timeout does not count time in deep sleep. If you don't want // the device to sleep, hold a wakelock - public NativeDaemonEvent remove(int cmdNum, int timeoutMs, String origCmd) { + public NativeDaemonEvent remove(int cmdNum, int timeoutMs, String logCmd) { PendingCmd found = null; synchronized (mPendingCmds) { for (PendingCmd pendingCmd : mPendingCmds) { @@ -522,7 +556,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo } } if (found == null) { - found = new PendingCmd(cmdNum, origCmd); + found = new PendingCmd(cmdNum, logCmd); mPendingCmds.add(found); } found.availableResponseCount--; @@ -544,7 +578,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo pw.println("Pending requests:"); synchronized (mPendingCmds) { for (PendingCmd pendingCmd : mPendingCmds) { - pw.println(" Cmd " + pendingCmd.cmdNum + " - " + pendingCmd.request); + pw.println(" Cmd " + pendingCmd.cmdNum + " - " + pendingCmd.logCmd); } } } diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index a78d68b6c163..1622f0f7d372 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -34,7 +34,6 @@ import static com.android.server.NetworkManagementService.NetdResponseCode.Tethe import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult; import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED; -import android.bluetooth.BluetoothTetheringDataTracker; import android.content.Context; import android.net.INetworkManagementEventObserver; import android.net.InterfaceConfiguration; @@ -61,6 +60,7 @@ import com.android.internal.app.IBatteryStats; import com.android.internal.net.NetworkStatsFactory; import com.android.internal.util.Preconditions; import com.android.server.NativeDaemonConnector.Command; +import com.android.server.NativeDaemonConnector.SensitiveArg; import com.android.server.net.LockdownVpnTracker; import com.google.android.collect.Maps; @@ -1001,7 +1001,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub } else { mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID, "broadcast", getSecurityType(wifiConfig), - wifiConfig.preSharedKey); + new SensitiveArg(wifiConfig.preSharedKey)); } mConnector.execute("softap", "startap"); } catch (NativeDaemonConnectorException e) { @@ -1051,7 +1051,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub } else { mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID, "broadcast", getSecurityType(wifiConfig), - wifiConfig.preSharedKey); + new SensitiveArg(wifiConfig.preSharedKey)); } } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java index d03da20f2219..d79211ccf5b5 100644 --- a/services/java/com/android/server/am/TaskRecord.java +++ b/services/java/com/android/server/am/TaskRecord.java @@ -57,7 +57,7 @@ final class TaskRecord extends ThumbnailHolder { /** Current stack */ ActivityStack stack; - private boolean mApplicationTask; + private boolean mApplicationTask = true; TaskRecord(int _taskId, ActivityInfo info, Intent _intent, ActivityStack _stack) { taskId = _taskId; @@ -164,7 +164,10 @@ final class TaskRecord extends ThumbnailHolder { // Was not previously in list. numFullscreen++; } - mApplicationTask = r.isApplicationActivity(); + // Only set this to be an application task if it has not already been set as home task. + if (mApplicationTask) { + mApplicationTask = r.isApplicationActivity(); + } mActivities.add(index, r); } diff --git a/services/java/com/android/server/content/SyncManager.java b/services/java/com/android/server/content/SyncManager.java index cf593ce630a0..80c7d88508c6 100644 --- a/services/java/com/android/server/content/SyncManager.java +++ b/services/java/com/android/server/content/SyncManager.java @@ -1966,6 +1966,10 @@ public class SyncManager { for (int i = 0, N = info.periodicSyncs.size(); i < N; i++) { final Bundle extras = info.periodicSyncs.get(i).first; final Long periodInMillis = info.periodicSyncs.get(i).second * 1000; + // Skip if the period is invalid + if (periodInMillis <= 0) { + continue; + } // find when this periodic sync was last scheduled to run final long lastPollTimeAbsolute = status.getPeriodicSyncTime(i); diff --git a/services/tests/servicestests/src/com/android/server/NativeDaemonConnectorTest.java b/services/tests/servicestests/src/com/android/server/NativeDaemonConnectorTest.java index 275d80764533..e2253a2151b0 100644 --- a/services/tests/servicestests/src/com/android/server/NativeDaemonConnectorTest.java +++ b/services/tests/servicestests/src/com/android/server/NativeDaemonConnectorTest.java @@ -17,10 +17,13 @@ package com.android.server; import static com.android.server.NativeDaemonConnector.appendEscaped; +import static com.android.server.NativeDaemonConnector.makeCommand; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; +import com.android.server.NativeDaemonConnector.SensitiveArg; + /** * Tests for {@link NativeDaemonConnector}. */ @@ -67,4 +70,28 @@ public class NativeDaemonConnectorTest extends AndroidTestCase { appendEscaped(builder, "caf\u00E9 c\u00F6ffee"); assertEquals("\"caf\u00E9 c\u00F6ffee\"", builder.toString()); } + + public void testSensitiveArgs() throws Exception { + final StringBuilder rawBuilder = new StringBuilder(); + final StringBuilder logBuilder = new StringBuilder(); + + rawBuilder.setLength(0); + logBuilder.setLength(0); + makeCommand(rawBuilder, logBuilder, 1, "foo", "bar", "baz"); + assertEquals("1 foo bar baz\0", rawBuilder.toString()); + assertEquals("1 foo bar baz", logBuilder.toString()); + + rawBuilder.setLength(0); + logBuilder.setLength(0); + makeCommand(rawBuilder, logBuilder, 1, "foo", new SensitiveArg("bar"), "baz"); + assertEquals("1 foo bar baz\0", rawBuilder.toString()); + assertEquals("1 foo [scrubbed] baz", logBuilder.toString()); + + rawBuilder.setLength(0); + logBuilder.setLength(0); + makeCommand(rawBuilder, logBuilder, 1, "foo", new SensitiveArg("foo bar"), "baz baz", + new SensitiveArg("wat")); + assertEquals("1 foo \"foo bar\" \"baz baz\" wat\0", rawBuilder.toString()); + assertEquals("1 foo [scrubbed] \"baz baz\" [scrubbed]", logBuilder.toString()); + } } diff --git a/tests/TransitionTests/AndroidManifest.xml b/tests/TransitionTests/AndroidManifest.xml index be6b145e2bc7..98174ab34e6c 100644 --- a/tests/TransitionTests/AndroidManifest.xml +++ b/tests/TransitionTests/AndroidManifest.xml @@ -212,6 +212,13 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + <activity android:label="DelayedTransition" + android:name="DelayedTransition"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> </application> diff --git a/tests/TransitionTests/res/layout/two_buttons.xml b/tests/TransitionTests/res/layout/two_buttons.xml new file mode 100644 index 000000000000..23d59f8584bd --- /dev/null +++ b/tests/TransitionTests/res/layout/two_buttons.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:id="@+id/container"> + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/button1"/> + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/button2"/> +</LinearLayout>
\ No newline at end of file diff --git a/tests/TransitionTests/src/com/android/transitiontests/DelayedTransition.java b/tests/TransitionTests/src/com/android/transitiontests/DelayedTransition.java new file mode 100644 index 000000000000..f05ea782bdf2 --- /dev/null +++ b/tests/TransitionTests/src/com/android/transitiontests/DelayedTransition.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.transitiontests; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; +import android.view.transition.TransitionManager; +import android.widget.Button; +import android.widget.LinearLayout; +import static android.widget.LinearLayout.LayoutParams; + +public class DelayedTransition extends Activity { + + private static final int SEARCH_SCREEN = 0; + private static final int RESULTS_SCREEN = 1; + ViewGroup mSceneRoot; + static int mCurrentScene; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.two_buttons); + + final Button button1 = (Button) findViewById(R.id.button1); + final Button button2 = (Button) findViewById(R.id.button2); + final LinearLayout container = (LinearLayout) findViewById(R.id.container); + button1.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + int buttonWidth = button1.getWidth(); + int containerWidth = container.getWidth(); + if (buttonWidth < containerWidth) { + TransitionManager.beginDelayedTransition(container, null); + button1.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT)); + TransitionManager.beginDelayedTransition(container, null); + button2.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.MATCH_PARENT)); + } else { + TransitionManager.beginDelayedTransition(container, null); + button1.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT)); + TransitionManager.beginDelayedTransition(container, null); + button2.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT)); + } + } + }); + } + +} |