diff options
39 files changed, 579 insertions, 195 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 0b082def0c8a..bff4d7dad888 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6026,6 +6026,17 @@ public final class Settings { public static final String ASSIST_SCREENSHOT_ENABLED = "assist_screenshot_enabled"; /** + * Specifies whether the screen will show an animation if screen contents are sent to the + * assist application (active voice interaction service). + * + * Note that the disclosure will be forced for third-party assistants or if the device + * does not support disabling it. + * + * @hide + */ + public static final String ASSIST_DISCLOSURE_ENABLED = "assist_disclosure_enabled"; + + /** * Names of the service components that the current user has explicitly allowed to * see all of the user's notifications, separated by ':'. * diff --git a/core/java/android/util/TimedRemoteCaller.java b/core/java/android/util/TimedRemoteCaller.java index abb2b6401d71..100c16e7f990 100644 --- a/core/java/android/util/TimedRemoteCaller.java +++ b/core/java/android/util/TimedRemoteCaller.java @@ -17,6 +17,7 @@ package android.util; import android.os.SystemClock; +import com.android.internal.annotations.GuardedBy; import java.util.concurrent.TimeoutException; @@ -29,8 +30,7 @@ import java.util.concurrent.TimeoutException; * the sequence number. If the response comes within the timeout * and its sequence number is the one sent in the method invocation, * then the call succeeded. If the response does not come within - * the timeout then the call failed. Older result received when - * waiting for the result are ignored. + * the timeout then the call failed. * <p> * Typical usage is: * </p> @@ -66,66 +66,90 @@ public abstract class TimedRemoteCaller<T> { public static final long DEFAULT_CALL_TIMEOUT_MILLIS = 5000; - private static final int UNDEFINED_SEQUENCE = -1; - private final Object mLock = new Object(); private final long mCallTimeoutMillis; - private int mSequenceCounter; - - private int mReceivedSequence = UNDEFINED_SEQUENCE; + /** The callbacks we are waiting for, key == sequence id, value == 1 */ + @GuardedBy("mLock") + private final SparseIntArray mAwaitedCalls = new SparseIntArray(1); - private int mAwaitedSequence = UNDEFINED_SEQUENCE; + /** The callbacks we received but for which the result has not yet been reported */ + @GuardedBy("mLock") + private final SparseArray<T> mReceivedCalls = new SparseArray<>(1); - private T mResult; + @GuardedBy("mLock") + private int mSequenceCounter; + /** + * Create a new timed caller. + * + * @param callTimeoutMillis The time to wait in {@link #getResultTimed} before a timed call will + * be declared timed out + */ public TimedRemoteCaller(long callTimeoutMillis) { mCallTimeoutMillis = callTimeoutMillis; } - public final int onBeforeRemoteCall() { + /** + * Indicate that a timed call will be made. + * + * @return The sequence id for the call + */ + protected final int onBeforeRemoteCall() { synchronized (mLock) { - mAwaitedSequence = mSequenceCounter++; - return mAwaitedSequence; - } - } + int sequenceId; + do { + sequenceId = mSequenceCounter++; + } while (mAwaitedCalls.get(sequenceId) != 0); - public final T getResultTimed(int sequence) throws TimeoutException { - synchronized (mLock) { - final boolean success = waitForResultTimedLocked(sequence); - if (!success) { - throw new TimeoutException("No reponse for sequence: " + sequence); - } - T result = mResult; - mResult = null; - return result; + mAwaitedCalls.put(sequenceId, 1); + + return sequenceId; } } - public final void onRemoteMethodResult(T result, int sequence) { + /** + * Indicate that the timed call has returned. + * + * @param result The result of the timed call + * @param sequence The sequence id of the call (from {@link #onBeforeRemoteCall()}) + */ + protected final void onRemoteMethodResult(T result, int sequence) { synchronized (mLock) { - if (sequence == mAwaitedSequence) { - mReceivedSequence = sequence; - mResult = result; + // Do nothing if we do not await the call anymore as it must have timed out + boolean containedSequenceId = mAwaitedCalls.get(sequence) != 0; + if (containedSequenceId) { + mAwaitedCalls.delete(sequence); + mReceivedCalls.put(sequence, result); mLock.notifyAll(); } } } - private boolean waitForResultTimedLocked(int sequence) { + /** + * Wait until the timed call has returned. + * + * @param sequence The sequence id of the call (from {@link #onBeforeRemoteCall()}) + * + * @return The result of the timed call (set in {@link #onRemoteMethodResult(Object, int)}) + */ + protected final T getResultTimed(int sequence) throws TimeoutException { final long startMillis = SystemClock.uptimeMillis(); while (true) { try { - if (mReceivedSequence == sequence) { - return true; - } - final long elapsedMillis = SystemClock.uptimeMillis() - startMillis; - final long waitMillis = mCallTimeoutMillis - elapsedMillis; - if (waitMillis <= 0) { - return false; + synchronized (mLock) { + if (mReceivedCalls.indexOfKey(sequence) >= 0) { + return mReceivedCalls.removeReturnOld(sequence); + } + final long elapsedMillis = SystemClock.uptimeMillis() - startMillis; + final long waitMillis = mCallTimeoutMillis - elapsedMillis; + if (waitMillis <= 0) { + mAwaitedCalls.delete(sequence); + throw new TimeoutException("No response for sequence: " + sequence); + } + mLock.wait(waitMillis); } - mLock.wait(waitMillis); } catch (InterruptedException ie) { /* ignore */ } diff --git a/core/java/android/view/SoundEffectConstants.java b/core/java/android/view/SoundEffectConstants.java index 4a77af47170d..8d891bbc2cfc 100644 --- a/core/java/android/view/SoundEffectConstants.java +++ b/core/java/android/view/SoundEffectConstants.java @@ -37,6 +37,8 @@ public class SoundEffectConstants { * or {@link View#FOCUS_BACKWARD} * @return The appropriate sonification constant. + * @throws {@link IllegalArgumentException} when the passed direction is not one of the + * documented values. */ public static int getContantForFocusDirection(int direction) { switch (direction) { diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java index 745da51a1dc1..bb771000b343 100644 --- a/core/java/android/view/TextureView.java +++ b/core/java/android/view/TextureView.java @@ -229,7 +229,6 @@ public class TextureView extends View { @Override protected void destroyHardwareResources() { destroyHardwareLayer(); - mUpdateSurface = mSurface != null; } private void destroyHardwareLayer() { @@ -376,17 +375,17 @@ public class TextureView extends View { } mLayer = mAttachInfo.mThreadedRenderer.createTextureLayer(); - if (!mUpdateSurface) { + boolean createNewSurface = (mSurface == null); + if (createNewSurface) { // Create a new SurfaceTexture for the layer. mSurface = new SurfaceTexture(false); mLayer.setSurfaceTexture(mSurface); nCreateNativeWindow(mSurface); } mSurface.setDefaultBufferSize(getWidth(), getHeight()); - mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler); - if (mListener != null && !mUpdateSurface) { + if (mListener != null && createNewSurface) { mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight()); } mLayer.setLayerPaint(mLayerPaint); @@ -745,9 +744,11 @@ public class TextureView extends View { "released SurfaceTexture"); } if (mSurface != null) { + nDestroyNativeWindow(); mSurface.release(); } mSurface = surfaceTexture; + nCreateNativeWindow(mSurface); /* * If the view is visible and we already made a layer, update the diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index c37cf569a893..5a7f0ff08782 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -20656,7 +20656,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <li>{@link #DRAG_FLAG_GLOBAL}</li> * <li>{@link #DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION}</li> * <li>{@link #DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION}</li> - * <li>{@link #DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION}</li> * <li>{@link #DRAG_FLAG_GLOBAL_URI_READ}</li> * <li>{@link #DRAG_FLAG_GLOBAL_URI_WRITE}</li> * <li>{@link #DRAG_FLAG_OPAQUE}</li> @@ -20674,6 +20673,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Log.w(VIEW_LOG_TAG, "startDragAndDrop called on a detached view."); return false; } + + data.prepareToLeaveProcess((flags & View.DRAG_FLAG_GLOBAL) != 0); + boolean okay = false; Point shadowSize = new Point(); @@ -23864,7 +23866,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * on the screen. */ private boolean shouldDrawRoundScrollbar() { - if (!mResources.getConfiguration().isScreenRound()) { + if (!mResources.getConfiguration().isScreenRound() || mAttachInfo == null) { return false; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 2bc1d74ffb79..757727b6f1f8 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -5539,6 +5539,10 @@ public final class ViewRootImpl implements ViewParent, // Remember who the current drag target is pre-dispatch final View prevDragView = mCurrentDragView; + if (what == DragEvent.ACTION_DROP) { + event.getClipData().prepareToEnterProcess(); + } + // Now dispatch the drag/drop event boolean result = mView.dispatchDragEvent(event); diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index f1fa21694976..0b9f9d09b483 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -484,6 +484,7 @@ public interface WindowManagerPolicy { public void switchInputMethod(boolean forwardDirection); public void shutdown(boolean confirm); + public void reboot(boolean confirm); public void rebootSafeMode(boolean confirm); /** diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java index 56c5cc9b69ec..294007946c77 100644 --- a/core/java/com/android/internal/app/AssistUtils.java +++ b/core/java/com/android/internal/app/AssistUtils.java @@ -16,10 +16,13 @@ package com.android.internal.app; +import com.android.internal.R; + import android.app.SearchManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Bundle; @@ -166,4 +169,41 @@ public class AssistUtils { return null; } + public static boolean isPreinstalledAssistant(Context context, ComponentName assistant) { + if (assistant == null) { + return false; + } + ApplicationInfo applicationInfo; + try { + applicationInfo = context.getPackageManager().getApplicationInfo( + assistant.getPackageName(), 0); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + return applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp(); + } + + private static boolean isDisclosureEnabled(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.ASSIST_DISCLOSURE_ENABLED, 0) != 0; + } + + /** + * @return if the disclosure animation should trigger for the given assistant. + * + * Third-party assistants will always need to disclose, while the user can configure this for + * pre-installed assistants. + */ + public static boolean shouldDisclose(Context context, ComponentName assistant) { + if (!allowDisablingAssistDisclosure(context)) { + return true; + } + + return isDisclosureEnabled(context) || !isPreinstalledAssistant(context, assistant); + } + + public static boolean allowDisablingAssistDisclosure(Context context) { + return context.getResources().getBoolean( + com.android.internal.R.bool.config_allowDisablingAssistDisclosure); + } } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index fef4a5381485..0aa3a7e9c8a4 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -111,7 +111,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 149 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 150 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -1425,22 +1425,6 @@ public class BatteryStatsImpl extends BatteryStats { mUnpluggedReportedCount = 0; return true; } - - @Override - public void writeSummaryFromParcelLocked(Parcel out, long batteryRealtime) { - super.writeSummaryFromParcelLocked(out, batteryRealtime); - out.writeLong(mCurrentReportedTotalTime); - out.writeInt(mCurrentReportedCount); - out.writeInt(mTrackingReportedValues ? 1 : 0); - } - - @Override - public void readSummaryFromParcelLocked(Parcel in) { - super.readSummaryFromParcelLocked(in); - mUnpluggedReportedTotalTime = mCurrentReportedTotalTime = in.readLong(); - mUnpluggedReportedCount = mCurrentReportedCount = in.readInt(); - mTrackingReportedValues = in.readInt() == 1; - } } /** diff --git a/core/res/res/drawable/ic_lock_power_off.xml b/core/res/res/drawable/ic_lock_power_off.xml index 718f17ed0c19..babd1be220cb 100644 --- a/core/res/res/drawable/ic_lock_power_off.xml +++ b/core/res/res/drawable/ic_lock_power_off.xml @@ -1,19 +1,25 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2014 The Android Open Source Project +<!-- + Copyright (C) 2016 The Android Open Source Project - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + 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 + 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. + 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. --> - -<bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ic_lock_power_off_alpha" - android:tint="?attr/colorControlNormal" /> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportHeight="24.0" + android:viewportWidth="24.0" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="#FF000000" + android:pathData="M13.0,3.0l-2.0,0.0l0.0,10.0l2.0,0.0L13.0,3.0zm4.83,2.17l-1.42,1.42C17.99,7.86 19.0,9.81 19.0,12.0c0.0,3.87 -3.13,7.0 -7.0,7.0s-7.0,-3.13 -7.0,-7.0c0.0,-2.19 1.01,-4.14 2.58,-5.42L6.17,5.17C4.23,6.82 3.0,9.26 3.0,12.0c0.0,4.97 4.03,9.0 9.0,9.0s9.0,-4.03 9.0,-9.0c0.0,-2.74 -1.23,-5.18 -3.17,-6.83z"/> +</vector> diff --git a/core/res/res/drawable/ic_restart.xml b/core/res/res/drawable/ic_restart.xml new file mode 100644 index 000000000000..47ac483fa311 --- /dev/null +++ b/core/res/res/drawable/ic_restart.xml @@ -0,0 +1,28 @@ +<!-- + Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportHeight="24.0" + android:viewportWidth="24.0" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="#FF000000" + android:pathData="M12.0,4.0L12.0,1.0L8.0,5.0l4.0,4.0L12.0,6.0c3.9,0.0 7.0,3.1 7.0,7.0c0.0,3.9 -3.1,7.0 -7.0,7.0l0.0,2.0c5.0,0.0 9.0,-4.0 9.0,-9.0C21.0,8.0 17.0,4.0 12.0,4.0z"/> + <path + android:fillColor="#FF000000" + android:pathData="M5.0,12.9C5.0,11.0 5.8,9.2 7.2,7.9L5.8,6.4C4.0,8.1 3.0,10.5 3.0,12.9c0.0,4.0 2.7,7.6 6.5,8.7l0.5,-1.9C7.1,18.8 5.0,16.1 5.0,12.9z"/> +</vector> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 5b68ee139e93..6161494353f2 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2091,9 +2091,11 @@ "bugreport" = Take bug report, if available "silent" = silent mode "users" = list of users + "restart" = restart device --> <string-array translatable="false" name="config_globalActionsList"> <item>power</item> + <item>restart</item> <item>bugreport</item> <item>users</item> </string-array> @@ -2520,6 +2522,10 @@ <!-- Flag indicating whether round icons should be parsed from the application manifest. --> <bool name="config_useRoundIcon">false</bool> + <!-- Flag indicating whether the assist disclosure can be disabled using + ASSIST_DISCLOSURE_ENABLED. --> + <bool name="config_allowDisablingAssistDisclosure">false</bool> + <!-- True if the device supports system navigation keys. --> <bool name="config_supportSystemNavigationKeys">false</bool> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 5a9099b8bf6a..c4594e5bdfe7 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -484,6 +484,10 @@ <!-- label for item that turns off power in phone options dialog --> <string name="global_action_power_off">Power off</string> + <!-- label for item that restarts phone in phone options dialog --> + <!-- TODO: promote to separate string--> + <string name="global_action_restart" translatable="false">@string/sim_restart_button</string> + <!-- label for item that generates a bug report in the phone options dialog --> <string name="global_action_bug_report">Bug report</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 3486b1b9524f..3c2b55b033b9 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1593,6 +1593,7 @@ <java-symbol type="string" name="bugreport_title" /> <java-symbol type="string" name="faceunlock_multiple_failures" /> <java-symbol type="string" name="global_action_power_off" /> + <java-symbol type="string" name="global_action_restart" /> <java-symbol type="string" name="global_actions_airplane_mode_off_status" /> <java-symbol type="string" name="global_actions_airplane_mode_on_status" /> <java-symbol type="string" name="global_actions_toggle_airplane_mode" /> @@ -2683,6 +2684,7 @@ <java-symbol type="drawable" name="ic_doc_generic" /> <java-symbol type="bool" name="config_nightDisplayAvailable" /> + <java-symbol type="bool" name="config_allowDisablingAssistDisclosure" /> <java-symbol type="integer" name="config_defaultNightDisplayAutoMode" /> <java-symbol type="integer" name="config_defaultNightDisplayCustomStartTime" /> <java-symbol type="integer" name="config_defaultNightDisplayCustomEndTime" /> @@ -2692,4 +2694,7 @@ <java-symbol type="bool" name="config_permissionReviewRequired" /> + + <java-symbol type="drawable" name="ic_restart" /> + </resources> diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java index ce6879d0ef4e..b4afddab97c9 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java @@ -178,19 +178,40 @@ public class BatteryStatsSamplingTimerTest extends TestCase { clocks.elapsedRealtime() * 1000); offBatterySummaryParcel.setDataPosition(0); + // Set the timebase running again. That way any issues with tracking reported values + // get tested when we unparcel the timers below. + timeBase.setRunning(true, clocks.uptimeMillis(), clocks.elapsedRealtime()); + // Read the on battery summary from the parcel. - BatteryStatsImpl.SamplingTimer unparceledTimer = new BatteryStatsImpl.SamplingTimer( - clocks, timeBase); - unparceledTimer.readSummaryFromParcelLocked(onBatterySummaryParcel); + BatteryStatsImpl.SamplingTimer unparceledOnBatteryTimer = + new BatteryStatsImpl.SamplingTimer(clocks, timeBase); + unparceledOnBatteryTimer.readSummaryFromParcelLocked(onBatterySummaryParcel); - assertEquals(10, unparceledTimer.getTotalTimeLocked(0, BatteryStats.STATS_SINCE_CHARGED)); - assertEquals(1, unparceledTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + assertEquals(10, unparceledOnBatteryTimer.getTotalTimeLocked(0, + BatteryStats.STATS_SINCE_CHARGED)); + assertEquals(1, unparceledOnBatteryTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); // Read the off battery summary from the parcel. - unparceledTimer = new BatteryStatsImpl.SamplingTimer(clocks, timeBase); - unparceledTimer.readSummaryFromParcelLocked(offBatterySummaryParcel); + BatteryStatsImpl.SamplingTimer unparceledOffBatteryTimer = + new BatteryStatsImpl.SamplingTimer(clocks, timeBase); + unparceledOffBatteryTimer.readSummaryFromParcelLocked(offBatterySummaryParcel); + + assertEquals(10, unparceledOffBatteryTimer.getTotalTimeLocked(0, + BatteryStats.STATS_SINCE_CHARGED)); + assertEquals(1, unparceledOffBatteryTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); - assertEquals(10, unparceledTimer.getTotalTimeLocked(0, BatteryStats.STATS_SINCE_CHARGED)); - assertEquals(1, unparceledTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + // Now, just like with a fresh timer, the first update should be absorbed to account for + // data being collected when we weren't recording. + unparceledOnBatteryTimer.update(10, 10); + + assertEquals(10, unparceledOnBatteryTimer.getTotalTimeLocked(0, + BatteryStats.STATS_SINCE_CHARGED)); + assertEquals(1, unparceledOnBatteryTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + + unparceledOffBatteryTimer.update(10, 10); + + assertEquals(10, unparceledOffBatteryTimer.getTotalTimeLocked(0, + BatteryStats.STATS_SINCE_CHARGED)); + assertEquals(1, unparceledOffBatteryTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); } } diff --git a/libs/common_time/common_time_server.cpp b/libs/common_time/common_time_server.cpp index f72ffaa63712..b1495effbc81 100644 --- a/libs/common_time/common_time_server.cpp +++ b/libs/common_time/common_time_server.cpp @@ -615,12 +615,11 @@ bool CommonTimeServer::handlePacket() { ssize_t recvBytes = recvfrom( mSocket, buf, sizeof(buf), 0, - reinterpret_cast<const sockaddr *>(&srcAddr), &srcAddrLen); + reinterpret_cast<sockaddr *>(&srcAddr), &srcAddrLen); if (recvBytes < 0) { - mBadPktLog.log(ANDROID_LOG_ERROR, LOG_TAG, - "recvfrom failed (res %d, errno %d)", - recvBytes, errno); + mBadPktLog.log(ANDROID_LOG_ERROR, LOG_TAG, "recvfrom failed (%s)", + strerror(errno)); return false; } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java index b75f52987a92..ddccc14a60f0 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java @@ -42,16 +42,21 @@ import java.util.List; * Displays an alphanumeric (latin-1) key entry for the user to enter * an unlock password */ - public class KeyguardPasswordView extends KeyguardAbsKeyInputView implements KeyguardSecurityView, OnEditorActionListener, TextWatcher { private final boolean mShowImeAtScreenOn; private final int mDisappearYTranslation; + // A delay constant to be used in a workaround for the situation where InputMethodManagerService + // is not switched to the new user yet. + // TODO: Remove this by ensuring such a race condition never happens. + private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms + InputMethodManager mImm; private TextView mPasswordEntry; private TextViewInputDisabler mPasswordEntryDisabler; + private View mSwitchImeButton; private Interpolator mLinearOutSlowInInterpolator; private Interpolator mFastOutLinearInInterpolator; @@ -141,12 +146,31 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView mPasswordEntry.requestFocus(); } + private void updateSwitchImeButton() { + // If there's more than one IME, enable the IME switcher button + final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE; + final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(mImm, false); + if (wasVisible != shouldBeVisible) { + mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE); + } + + // TODO: Check if we still need this hack. + // If no icon is visible, reset the start margin on the password field so the text is + // still centered. + if (mSwitchImeButton.getVisibility() != View.VISIBLE) { + android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams(); + if (params instanceof MarginLayoutParams) { + final MarginLayoutParams mlp = (MarginLayoutParams) params; + mlp.setMarginStart(0); + mPasswordEntry.setLayoutParams(params); + } + } + } + @Override protected void onFinishInflate() { super.onFinishInflate(); - boolean imeOrDeleteButtonVisible = false; - mImm = (InputMethodManager) getContext().getSystemService( Context.INPUT_METHOD_SERVICE); @@ -171,31 +195,29 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView mPasswordEntry.requestFocus(); + mSwitchImeButton = findViewById(R.id.switch_ime_button); + mSwitchImeButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mCallback.userActivity(); // Leave the screen on a bit longer + // Do not show auxiliary subtypes in password lock screen. + mImm.showInputMethodPicker(false /* showAuxiliarySubtypes */); + } + }); + // If there's more than one IME, enable the IME switcher button - View switchImeButton = findViewById(R.id.switch_ime_button); - if (switchImeButton != null && hasMultipleEnabledIMEsOrSubtypes(mImm, false)) { - switchImeButton.setVisibility(View.VISIBLE); - imeOrDeleteButtonVisible = true; - switchImeButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - mCallback.userActivity(); // Leave the screen on a bit longer - // Do not show auxiliary subtypes in password lock screen. - mImm.showInputMethodPicker(false /* showAuxiliarySubtypes */); - } - }); - } + updateSwitchImeButton(); - // If no icon is visible, reset the start margin on the password field so the text is - // still centered. - if (!imeOrDeleteButtonVisible) { - android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams(); - if (params instanceof MarginLayoutParams) { - final MarginLayoutParams mlp = (MarginLayoutParams) params; - mlp.setMarginStart(0); - mPasswordEntry.setLayoutParams(params); + // When we the current user is switching, InputMethodManagerService sometimes has not + // switched internal state yet here. As a quick workaround, we check the keyboard state + // again. + // TODO: Remove this workaround by ensuring such a race condition never happens. + postDelayed(new Runnable() { + @Override + public void run() { + updateSwitchImeButton(); } - } + }, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON); } @Override diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 3838143cc193..3bdb6fbdb3cb 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -863,4 +863,7 @@ <!-- Label for Help and feedback menu item --> <string name="help_feedback_label">Help & feedback</string> + <!-- Content description for drawer menu button [CHAR_LIMIT=30]--> + <string name="content_description_menu_button">Menu</string> + </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java index 6658c14bd1cd..a50b366c26ac 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java @@ -226,6 +226,7 @@ public class SettingsDrawerActivity extends Activity { public void showMenuIcon() { mShowingMenu = true; getActionBar().setHomeAsUpIndicator(R.drawable.ic_menu); + getActionBar().setHomeActionContentDescription(R.string.content_description_menu_button); getActionBar().setDisplayHomeAsUpEnabled(true); } diff --git a/packages/Shell/tests/Android.mk b/packages/Shell/tests/Android.mk index 1e0eaace35da..872eb7ac7dcf 100644 --- a/packages/Shell/tests/Android.mk +++ b/packages/Shell/tests/Android.mk @@ -8,7 +8,9 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := ub-uiautomator +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-test \ + ub-uiautomator \ LOCAL_PACKAGE_NAME := ShellTests LOCAL_INSTRUMENTATION_FOR := Shell diff --git a/packages/Shell/tests/AndroidManifest.xml b/packages/Shell/tests/AndroidManifest.xml index 54b0802e1504..6d564c640fcd 100644 --- a/packages/Shell/tests/AndroidManifest.xml +++ b/packages/Shell/tests/AndroidManifest.xml @@ -36,7 +36,7 @@ </activity> </application> - <instrumentation android:name="android.test.InstrumentationTestRunner" + <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" android:targetPackage="com.android.shell" android:label="Tests for Shell" /> diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java index 902c71d64ae9..dde71ebe5530 100644 --- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java +++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java @@ -36,6 +36,13 @@ import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_STARTE import static com.android.shell.BugreportProgressService.POLLING_FREQUENCY; import static com.android.shell.BugreportProgressService.SCREENSHOT_DELAY_SECONDS; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; @@ -56,6 +63,13 @@ import java.util.zip.ZipOutputStream; import libcore.io.Streams; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.junit.runner.RunWith; + import android.app.ActivityManager; import android.app.ActivityManager.RunningServiceInfo; import android.app.Instrumentation; @@ -68,11 +82,12 @@ import android.os.Bundle; import android.os.SystemClock; import android.os.SystemProperties; import android.service.notification.StatusBarNotification; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject; import android.support.test.uiautomator.UiObjectNotFoundException; -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; @@ -96,7 +111,8 @@ import com.android.shell.ActionSendMultipleConsumerActivity.CustomActionSendMult * <strong>NOTE</strong>: these tests only work if the device is unlocked. */ @LargeTest -public class BugreportReceiverTest extends InstrumentationTestCase { +@RunWith(AndroidJUnit4.class) +public class BugreportReceiverTest { private static final String TAG = "BugreportReceiverTest"; // Timeout for UI operations, in milliseconds. @@ -149,9 +165,10 @@ public class BugreportReceiverTest extends InstrumentationTestCase { private UiBot mUiBot; private CustomActionSendMultipleListener mListener; - @Override - protected void setUp() throws Exception { - super.setUp(); + @Rule public TestName mName = new TestName(); + + @Before + public void setUp() throws Exception { Log.i(TAG, getName() + ".setup()"); Instrumentation instrumentation = getInstrumentation(); mContext = instrumentation.getTargetContext(); @@ -181,13 +198,13 @@ public class BugreportReceiverTest extends InstrumentationTestCase { mUiBot.turnScreenOn(); } - @Override - protected void tearDown() throws Exception { + @After + public void tearDown() throws Exception { Log.i(TAG, getName() + ".tearDown()"); cancelExistingNotifications(); - super.tearDown(); } + @Test public void testProgress() throws Exception { resetProperties(); sendBugreportStarted(1000); @@ -233,6 +250,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase { assertServiceNotRunning(); } + @Test public void testProgress_cancel() throws Exception { resetProperties(); sendBugreportStarted(1000); @@ -249,6 +267,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase { waitForService(false); } + @Test public void testProgress_takeExtraScreenshot() throws Exception { resetProperties(); sendBugreportStarted(1000); @@ -267,6 +286,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase { assertServiceNotRunning(); } + @Test public void testScreenshotFinishesAfterBugreport() throws Exception { resetProperties(); @@ -286,6 +306,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase { assertServiceNotRunning(); } + @Test public void testProgress_changeDetailsInvalidInput() throws Exception { resetProperties(); sendBugreportStarted(1000); @@ -331,6 +352,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase { assertServiceNotRunning(); } + @Test public void testProgress_cancelBugClosesDetailsDialog() throws Exception { resetProperties(); sendBugreportStarted(1000); @@ -346,10 +368,12 @@ public class BugreportReceiverTest extends InstrumentationTestCase { assertServiceNotRunning(); } + @Test public void testProgress_changeDetailsPlainBugreport() throws Exception { changeDetailsTest(true); } + @Test public void testProgress_changeDetailsZippedBugreport() throws Exception { changeDetailsTest(false); } @@ -383,10 +407,12 @@ public class BugreportReceiverTest extends InstrumentationTestCase { assertServiceNotRunning(); } + @Test public void testProgress_changeJustDetailsTouchingDetails() throws Exception { changeJustDetailsTest(true); } + @Test public void testProgress_changeJustDetailsTouchingNotification() throws Exception { changeJustDetailsTest(false); } @@ -410,6 +436,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase { assertServiceNotRunning(); } + @Test public void testProgress_changeJustDetailsIsClearedOnSecondBugreport() throws Exception { resetProperties(); sendBugreportStarted(ID, PID, NAME, 1000); @@ -453,6 +480,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase { * Tests the scenario where the initial screenshot and dumpstate are finished while the user * is changing the info in the details screen. */ + @Test public void testProgress_bugreportAndScreenshotFinishedWhileChangingDetails() throws Exception { bugreportFinishedWhileChangingDetailsTest(false); } @@ -461,6 +489,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase { * Tests the scenario where dumpstate is finished while the user is changing the info in the * details screen, but the initial screenshot finishes afterwards. */ + @Test public void testProgress_bugreportFinishedWhileChangingDetails() throws Exception { bugreportFinishedWhileChangingDetailsTest(true); } @@ -500,14 +529,17 @@ public class BugreportReceiverTest extends InstrumentationTestCase { assertServiceNotRunning(); } + @Test public void testBugreportFinished_withWarningFirstTime() throws Exception { bugreportFinishedWithWarningTest(null); } + @Test public void testBugreportFinished_withWarningUnknownState() throws Exception { bugreportFinishedWithWarningTest(STATE_UNKNOWN); } + @Test public void testBugreportFinished_withWarningShowAgain() throws Exception { bugreportFinishedWithWarningTest(STATE_SHOW); } @@ -560,6 +592,7 @@ public class BugreportReceiverTest extends InstrumentationTestCase { assertEquals("Didn't change state", STATE_HIDE, newState); } + @Test public void testShareBugreportAfterServiceDies() throws Exception { sendBugreportFinished(NO_ID, mPlainTextPath, NO_SCREENSHOT); waitForService(false); @@ -567,21 +600,25 @@ public class BugreportReceiverTest extends InstrumentationTestCase { assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT); } + @Test public void testBugreportFinished_plainBugreportAndScreenshot() throws Exception { Bundle extras = sendBugreportFinishedAndGetSharedIntent(mPlainTextPath, mScreenshotPath); assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT); } + @Test public void testBugreportFinished_zippedBugreportAndScreenshot() throws Exception { Bundle extras = sendBugreportFinishedAndGetSharedIntent(mZipPath, mScreenshotPath); assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT); } + @Test public void testBugreportFinished_plainBugreportAndNoScreenshot() throws Exception { Bundle extras = sendBugreportFinishedAndGetSharedIntent(mPlainTextPath, NO_SCREENSHOT); assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT); } + @Test public void testBugreportFinished_zippedBugreportAndNoScreenshot() throws Exception { Bundle extras = sendBugreportFinishedAndGetSharedIntent(mZipPath, NO_SCREENSHOT); assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT); @@ -1001,6 +1038,14 @@ public class BugreportReceiverTest extends InstrumentationTestCase { mUiBot.assertNotVisibleById("android:id/alertTitle"); } + private String getName() { + return mName.getMethodName(); + } + + private Instrumentation getInstrumentation() { + return InstrumentationRegistry.getInstrumentation(); + } + private static void sleep(long ms) { Log.d(TAG, "sleeping for " + ms + "ms"); SystemClock.sleep(ms); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java index ac90ce73fa03..19a5d522f530 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java @@ -237,6 +237,10 @@ public class QSContainer extends FrameLayout { mQSPanel.setListening(mListening && mQsExpanded); } + public void setHeaderListening(boolean listening) { + mHeader.setListening(listening); + } + public void setQsExpansion(float expansion, float headerTranslation) { if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation); mQsExpansion = expansion; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index b6597a025957..ed0fc1fee12a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -251,10 +251,12 @@ public class QSPanel extends LinearLayout implements Tunable, Callback { if (mListening) { refreshAllTiles(); } - if (listening) { - mBrightnessController.registerCallbacks(); - } else { - mBrightnessController.unregisterCallbacks(); + if (mBrightnessView.getVisibility() == View.VISIBLE) { + if (listening) { + mBrightnessController.registerCallbacks(); + } else { + mBrightnessController.unregisterCallbacks(); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java index 4638c8558ff7..1343c3ad0307 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java @@ -26,6 +26,7 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Rect; +import android.graphics.Xfermode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.DrawableWrapper; @@ -224,6 +225,12 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen } @Override + public void setXfermode(@Nullable Xfermode mode) { + // DrawableWrapper does not call this for us. + getDrawable().setXfermode(mode); + } + + @Override public int getIntrinsicWidth() { return -1; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index f27f8f53ad8d..812c5c151ce9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -1617,6 +1617,9 @@ public class NotificationPanelView extends PanelView implements if (mQsExpanded) { onQsExpansionStarted(); } + // Since there are QS tiles in the header now, we need to make sure we start listening + // immediately so they can be up to date. + mQsContainer.setHeaderListening(true); } @Override diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java index a6350fe8cf35..89705560db7d 100644 --- a/services/core/java/com/android/server/pm/SELinuxMMAC.java +++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java @@ -19,6 +19,7 @@ package com.android.server.pm; import android.content.pm.PackageParser; import android.content.pm.Signature; import android.os.Environment; +import android.os.SystemProperties; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; @@ -64,6 +65,8 @@ public final class SELinuxMMAC { // to synchronize access during policy load and access attempts. private static List<Policy> sPolicies = new ArrayList<>(); + private static final String PROP_FORCE_RESTORECON = "sys.force_restorecon"; + /** Path to version on rootfs */ private static final File VERSION_FILE = new File("/selinux_version"); @@ -322,6 +325,11 @@ public final class SELinuxMMAC { * @return Returns true if the restorecon should occur or false otherwise. */ public static boolean isRestoreconNeeded(File file) { + // To investigate boot timing, allow a property to always force restorecon + if (SystemProperties.getBoolean(PROP_FORCE_RESTORECON, false)) { + return true; + } + try { final byte[] buf = new byte[20]; final int len = Os.getxattr(file.getAbsolutePath(), XATTR_SEAPP_HASH, buf); diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java index 6fc15f0216e4..bb91f76ecf30 100644 --- a/services/core/java/com/android/server/policy/GlobalActions.java +++ b/services/core/java/com/android/server/policy/GlobalActions.java @@ -103,6 +103,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown"; private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist"; private static final String GLOBAL_ACTION_KEY_ASSIST = "assist"; + private static final String GLOBAL_ACTION_KEY_RESTART = "restart"; private final Context mContext; private final WindowManagerFuncs mWindowManagerFuncs; @@ -298,6 +299,8 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac mItems.add(getVoiceAssistAction()); } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) { mItems.add(getAssistAction()); + } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) { + mItems.add(new RestartAction()); } else { Log.e(TAG, "Invalid global action key " + actionKey); } @@ -369,6 +372,38 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac } } + private final class RestartAction extends SinglePressAction implements LongPressAction { + private RestartAction() { + super(R.drawable.ic_restart, R.string.global_action_restart); + } + + @Override + public boolean onLongPress() { + UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) { + mWindowManagerFuncs.rebootSafeMode(true); + return true; + } + return false; + } + + @Override + public boolean showDuringKeyguard() { + return true; + } + + @Override + public boolean showBeforeProvisioning() { + return true; + } + + @Override + public void onPress() { + mWindowManagerFuncs.reboot(false /* confirm */); + } + } + + private class BugReportAction extends SinglePressAction implements LongPressAction { public BugReportAction() { diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index e266b924cd0d..76f2f0e43e9b 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1400,9 +1400,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { wallpaper.whichPending = which; wallpaper.setComplete = completion; wallpaper.cropHint.set(cropHint); - if ((which & FLAG_SYSTEM) != 0) { - wallpaper.allowBackup = allowBackup; - } + wallpaper.allowBackup = allowBackup; } return pfd; } finally { diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 6515fbdd0587..34fa1b023dce 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -119,6 +119,8 @@ public class WindowAnimator { // check if some got replaced and can be removed. private boolean mRemoveReplacedWindows = false; + private final AppTokenList mTmpExitingAppTokens = new AppTokenList(); + private String forceHidingToString() { switch (mForceHiding) { case KEYGUARD_NOT_SHOWN: return "KEYGUARD_NOT_SHOWN"; @@ -189,10 +191,19 @@ public class WindowAnimator { } } - final AppTokenList exitingAppTokens = stack.mExitingAppTokens; - final int exitingCount = exitingAppTokens.size(); + mTmpExitingAppTokens.clear(); + mTmpExitingAppTokens.addAll(stack.mExitingAppTokens); + + final int exitingCount = mTmpExitingAppTokens.size(); for (int i = 0; i < exitingCount; i++) { - final AppWindowAnimator appAnimator = exitingAppTokens.get(i).mAppAnimator; + final AppWindowAnimator appAnimator = mTmpExitingAppTokens.get(i).mAppAnimator; + // stepAnimation can trigger finishExit->removeWindowInnerLocked + // ->performSurfacePlacement + // performSurfacePlacement will directly manipulate the mExitingAppTokens list + // so we need to iterate over a copy and check for modifications. + if (!stack.mExitingAppTokens.contains(appAnimator)) { + continue; + } appAnimator.wasAnimating = appAnimator.animating; if (appAnimator.stepAnimationLocked(mCurrentTime, displayId)) { setAnimating(true); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 1bcab99594b6..22120d016af4 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -4727,6 +4727,12 @@ public class WindowManagerService extends IWindowManager.Stub // Called by window manager policy. Not exposed externally. @Override + public void reboot(boolean confirm) { + ShutdownThread.reboot(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm); + } + + // Called by window manager policy. Not exposed externally. + @Override public void rebootSafeMode(boolean confirm) { ShutdownThread.rebootSafeMode(mContext, confirm); } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 0fcf0c71e474..dfee8de144d2 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -845,6 +845,22 @@ class WindowStateAnimator { mPendingDestroySurface = null; } + void applyMagnificationSpec(MagnificationSpec spec, Matrix transform) { + final int surfaceInsetLeft = mWin.mAttrs.surfaceInsets.left; + final int surfaceInsetTop = mWin.mAttrs.surfaceInsets.top; + + if (spec != null && !spec.isNop()) { + float scale = spec.scale; + transform.postScale(scale, scale); + transform.postTranslate(spec.offsetX, spec.offsetY); + + // As we are scaling the whole surface, to keep the content + // in the same position we will also have to scale the surfaceInsets. + transform.postTranslate(-(surfaceInsetLeft*scale - surfaceInsetLeft), + -(surfaceInsetTop*scale - surfaceInsetTop)); + } + } + void computeShownFrameLocked() { final boolean selfTransformation = mHasLocalTransformation; Transformation attachedTransformation = @@ -935,10 +951,7 @@ class WindowStateAnimator { if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) { MagnificationSpec spec = mService.mAccessibilityController .getMagnificationSpecForWindowLocked(mWin); - if (spec != null && !spec.isNop()) { - tmpMatrix.postScale(spec.scale, spec.scale); - tmpMatrix.postTranslate(spec.offsetX, spec.offsetY); - } + applyMagnificationSpec(spec, tmpMatrix); } // "convert" it into SurfaceFlinger's format @@ -1037,10 +1050,7 @@ class WindowStateAnimator { tmpMatrix.setScale(mWin.mGlobalScale, mWin.mGlobalScale); tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset); - if (spec != null && !spec.isNop()) { - tmpMatrix.postScale(spec.scale, spec.scale); - tmpMatrix.postTranslate(spec.offsetX, spec.offsetY); - } + applyMagnificationSpec(spec, tmpMatrix); tmpMatrix.getValues(tmpFloats); @@ -1896,11 +1906,13 @@ class WindowStateAnimator { if (mDeferTransactionUntilFrame < 0) { return; } + final WindowState parentWindow = mWin.getParentWindow(); long time = System.currentTimeMillis(); if (time > mDeferTransactionTime + PENDING_TRANSACTION_FINISH_WAIT_TIME) { mDeferTransactionTime = -1; mDeferTransactionUntilFrame = -1; - } else { + } else if (parentWindow != null && + parentWindow.mWinAnimator.hasSurface()) { mSurfaceController.deferTransactionUntil( mWin.getParentWindow().mWinAnimator.mSurfaceController.getHandle(), mDeferTransactionUntilFrame); diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index 73033589e308..6247089953ad 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -34,6 +34,12 @@ import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT; import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED; import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; @@ -74,7 +80,8 @@ import android.os.Binder; import android.os.INetworkManagementService; import android.os.PowerManagerInternal; import android.os.UserHandle; -import android.test.AndroidTestCase; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; import android.text.format.Time; import android.util.Log; import android.util.TrustedTime; @@ -86,6 +93,11 @@ import libcore.io.IoUtils; import com.google.common.util.concurrent.AbstractFuture; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -105,7 +117,8 @@ import java.util.concurrent.TimeoutException; /** * Tests for {@link NetworkPolicyManagerService}. */ -public class NetworkPolicyManagerServiceTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class NetworkPolicyManagerServiceTest { private static final String TAG = "NetworkPolicyManagerServiceTest"; private static final long TEST_START = 1194220800000L; @@ -116,7 +129,6 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { private BroadcastInterceptingContext mServiceContext; private File mPolicyDir; - private List<Class<?>> mLocalServices = new ArrayList<>(); private @Mock IActivityManager mActivityManager; private @Mock INetworkStatsService mStatsService; @@ -124,7 +136,6 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { private @Mock TrustedTime mTime; private @Mock IConnectivityManager mConnManager; private @Mock INotificationManager mNotifManager; - private @Mock UsageStatsManagerInternal mUsageStats; private @Mock PackageManager mPackageManager; private IUidObserver mUidObserver; @@ -146,12 +157,20 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { private static final String PKG_NAME_A = "name.is.A,pkg.A"; - public void setUp() throws Exception { - super.setUp(); + @BeforeClass + public static void registerLocalServices() { + addLocalServiceMock(PowerManagerInternal.class); + addLocalServiceMock(DeviceIdleController.LocalService.class); + final UsageStatsManagerInternal usageStats = + addLocalServiceMock(UsageStatsManagerInternal.class); + when(usageStats.getIdleUidsForUser(anyInt())).thenReturn(new int[]{}); + } + @Before + public void callSystemReady() throws Exception { MockitoAnnotations.initMocks(this); - final Context context = getContext(); + final Context context = InstrumentationRegistry.getContext(); setCurrentTimeMillis(TEST_START); @@ -183,10 +202,6 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { } }).when(mActivityManager).registerUidObserver(any(), anyInt()); - addLocalServiceMock(PowerManagerInternal.class); - addLocalServiceMock(DeviceIdleController.LocalService.class); - addLocalServiceMock(UsageStatsManagerInternal.class, mUsageStats); - mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager, mStatsService, mNetworkManager, mTime, mPolicyDir, true); mService.bindConnectivityManager(mConnManager); @@ -216,7 +231,6 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) .thenReturn(new ApplicationInfo()); when(mPackageManager.getPackagesForUid(UID_A)).thenReturn(new String[] {PKG_NAME_A}); - when(mUsageStats.getIdleUidsForUser(anyInt())).thenReturn(new int[]{}); when(mNetworkManager.isBandwidthControlEnabled()).thenReturn(true); expectCurrentTime(); @@ -230,28 +244,17 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { mNetworkObserver = networkObserver.getValue(); } - public void tearDown() throws Exception { + @After + public void removeFiles() throws Exception { for (File file : mPolicyDir.listFiles()) { file.delete(); } + } - mServiceContext = null; - mPolicyDir = null; - - mActivityManager = null; - mStatsService = null; - mTime = null; - - mService = null; - - // TODO: must remove services, otherwise next test will fail. - // JUnit4 would avoid that hack by using a static setup. - removeLocalServiceMocks(); - - // Added by NetworkPolicyManagerService's constructor. + @After + public void unregisterLocalServices() throws Exception { + // Registered by NetworkPolicyManagerService's constructor. LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class); - - super.tearDown(); } // NOTE: testPolicyChangeTriggersListener() and testUidForeground() are too superficial, they @@ -259,6 +262,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { // different modes (Data Saver, Battery Saver, Doze, App idle, etc...). // These scenarios are extensively tested on CTS' HostsideRestrictBackgroundNetworkTests. + @Test public void testPolicyChangeTriggersListener() throws Exception { mPolicyListener.expect().onRestrictBackgroundBlacklistChanged(anyInt(), anyBoolean()); @@ -268,6 +272,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { mPolicyListener.waitAndVerify().onRestrictBackgroundBlacklistChanged(APP_ID_A, true); } + @Test public void testUidForeground() throws Exception { // push all uids into background mUidObserver.onUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE); @@ -287,6 +292,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { assertTrue(mService.isUidForeground(UID_B)); } + @Test public void testLastCycleBoundaryThisMonth() throws Exception { // assume cycle day of "5th", which should be in same month final long currentTime = parseTime("2007-11-14T00:00:00.000Z"); @@ -298,6 +304,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { assertTimeEquals(expectedCycle, actualCycle); } + @Test public void testLastCycleBoundaryLastMonth() throws Exception { // assume cycle day of "20th", which should be in last month final long currentTime = parseTime("2007-11-14T00:00:00.000Z"); @@ -309,6 +316,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { assertTimeEquals(expectedCycle, actualCycle); } + @Test public void testLastCycleBoundaryThisMonthFebruary() throws Exception { // assume cycle day of "30th" in february; should go to january final long currentTime = parseTime("2007-02-14T00:00:00.000Z"); @@ -320,6 +328,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { assertTimeEquals(expectedCycle, actualCycle); } + @Test public void testLastCycleBoundaryLastMonthFebruary() throws Exception { // assume cycle day of "30th" in february, which should clamp final long currentTime = parseTime("2007-03-14T00:00:00.000Z"); @@ -331,6 +340,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { assertTimeEquals(expectedCycle, actualCycle); } + @Test public void testCycleBoundaryLeapYear() throws Exception { final NetworkPolicy policy = new NetworkPolicy( sTemplateWifi, 29, TIMEZONE_UTC, 1024L, 1024L, false); @@ -354,6 +364,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { computeNextCycleBoundary(parseTime("2007-03-14T00:00:00.000Z"), policy)); } + @Test public void testNextCycleTimezoneAfterUtc() throws Exception { // US/Central is UTC-6 final NetworkPolicy policy = new NetworkPolicy( @@ -362,6 +373,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { computeNextCycleBoundary(parseTime("2012-01-05T00:00:00.000Z"), policy)); } + @Test public void testNextCycleTimezoneBeforeUtc() throws Exception { // Israel is UTC+2 final NetworkPolicy policy = new NetworkPolicy( @@ -370,6 +382,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { computeNextCycleBoundary(parseTime("2012-01-05T00:00:00.000Z"), policy)); } + @Test public void testNextCycleSane() throws Exception { final NetworkPolicy policy = new NetworkPolicy( sTemplateWifi, 31, TIMEZONE_UTC, WARNING_DISABLED, LIMIT_DISABLED, false); @@ -385,6 +398,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { } } + @Test public void testLastCycleSane() throws Exception { final NetworkPolicy policy = new NetworkPolicy( sTemplateWifi, 31, TIMEZONE_UTC, WARNING_DISABLED, LIMIT_DISABLED, false); @@ -400,6 +414,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { } } + @Test public void testCycleTodayJanuary() throws Exception { final NetworkPolicy policy = new NetworkPolicy( sTemplateWifi, 14, "US/Pacific", 1024L, 1024L, false); @@ -419,6 +434,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { computeLastCycleBoundary(parseTime("2013-01-14T15:11:00.000-08:00"), policy)); } + @Test public void testLastCycleBoundaryDST() throws Exception { final long currentTime = parseTime("1989-01-02T07:30:00.000"); final long expectedCycle = parseTime("1988-12-03T02:00:00.000Z"); @@ -429,6 +445,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { assertTimeEquals(expectedCycle, actualCycle); } + @Test public void testNetworkPolicyAppliedCycleLastMonth() throws Exception { NetworkState[] state = null; NetworkStats stats = null; @@ -471,6 +488,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { verifySetInterfaceQuota(TEST_IFACE, (2 * MB_IN_BYTES) - 512); } + @Test public void testOverWarningLimitNotification() throws Exception { NetworkState[] state = null; NetworkStats stats = null; @@ -579,6 +597,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { } } + @Test public void testMeteredNetworkWithoutLimit() throws Exception { NetworkState[] state = null; NetworkStats stats = null; @@ -740,32 +759,13 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { /** * Creates a mock and registers it to {@link LocalServices}. */ - private <T> T addLocalServiceMock(Class<T> clazz) { + private static <T> T addLocalServiceMock(Class<T> clazz) { final T mock = mock(clazz); - return addLocalServiceMock(clazz, mock); - } - - /** - * Registers a mock to {@link LocalServices}. - */ - private <T> T addLocalServiceMock(Class<T> clazz, T mock) { LocalServices.addService(clazz, mock); - mLocalServices.add(clazz); return mock; } /** - * Unregisters all mocks from {@link LocalServices}. - */ - private void removeLocalServiceMocks() { - for (Class<?> clazz : mLocalServices) { - Log.d(TAG, "removeLocalServiceMock(): " + clazz.getName()); - LocalServices.removeServiceForTest(clazz); - } - mLocalServices.clear(); - } - - /** * Custom Mockito answer used to verify async {@link INetworkPolicyListener} calls. * * <p>Typical usage: diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 8560651fa7ec..efd479f394c5 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -563,6 +563,7 @@ public class UsbDeviceManager { mAccessoryModeRequestTime + ACCESSORY_REQUEST_TIMEOUT; if (mConfigured && enteringAccessoryMode) { + mAccessoryModeRequestTime = 0; // successfully entered accessory mode if (mAccessoryStrings != null) { diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java index b0cc2aca8981..c3075b3daa3e 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java @@ -46,6 +46,7 @@ import android.util.Slog; import android.view.IWindowManager; import android.view.WindowManager; +import com.android.internal.app.AssistUtils; import com.android.internal.app.IAssistScreenshotReceiver; import com.android.internal.app.IVoiceInteractionSessionShowCallback; import com.android.internal.app.IVoiceInteractor; @@ -301,7 +302,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { } else { mScreenshot = null; } - if (needDisclosure) { + if (needDisclosure && AssistUtils.shouldDisclose(mContext, mSessionComponentName)) { mHandler.post(mShowAssistDisclosureRunnable); } if (mSession != null) { diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java index d7d4e840aa77..f5e422df7511 100644 --- a/telephony/java/android/telephony/DisconnectCause.java +++ b/telephony/java/android/telephony/DisconnectCause.java @@ -212,6 +212,20 @@ public class DisconnectCause { */ public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53; + /** + * The call was terminated because cellular data has been disabled. + * Used when in a video call and the user disables cellular data via the settings. + * {@hide} + */ + public static final int DATA_DISABLED = 54; + + /** + * The call was terminated because the data policy has disabled cellular data. + * Used when in a video call and the user has exceeded the device data limit. + * {@hide} + */ + public static final int DATA_LIMIT_REACHED = 55; + //********************************************************************************************* // When adding a disconnect type: // 1) Please assign the new type the next id value below. @@ -220,14 +234,14 @@ public class DisconnectCause { // 4) Update toString() with the newly added disconnect type. // 5) Update android.telecom.DisconnectCauseUtil with any mappings to a telecom.DisconnectCause. // - // NextId: 54 + // NextId: 56 //********************************************************************************************* /** Smallest valid value for call disconnect codes. */ public static final int MINIMUM_VALID_VALUE = NOT_DISCONNECTED; /** Largest valid value for call disconnect codes. */ - public static final int MAXIMUM_VALID_VALUE = MAXIMUM_NUMBER_OF_CALLS_REACHED; + public static final int MAXIMUM_VALID_VALUE = DATA_LIMIT_REACHED; /** Private constructor to avoid class instantiation. */ private DisconnectCause() { @@ -343,6 +357,10 @@ public class DisconnectCause { return "ANSWERED_ELSEWHERE"; case MAXIMUM_NUMBER_OF_CALLS_REACHED: return "MAXIMUM_NUMER_OF_CALLS_REACHED"; + case DATA_DISABLED: + return "DATA_DISABLED"; + case DATA_LIMIT_REACHED: + return "DATA_LIMIT_REACHED"; default: return "INVALID: " + cause; } diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java index 408ad31674c4..56b882221cd7 100644 --- a/telephony/java/com/android/ims/ImsReasonInfo.java +++ b/telephony/java/com/android/ims/ImsReasonInfo.java @@ -298,6 +298,16 @@ public class ImsReasonInfo implements Parcelable { public static final int CODE_REMOTE_CALL_DECLINE = 1404; /** + * Indicates the call was disconnected due to the user reaching their data limit. + */ + public static final int CODE_DATA_LIMIT_REACHED = 1405; + + /** + * Indicates the call was disconnected due to the user disabling cellular data. + */ + public static final int CODE_DATA_DISABLED = 1406; + + /** * Network string error messages. * mExtraMessage may have these values. */ diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp index 6ec88cbe3b0f..379c991c98d2 100644 --- a/tools/aapt2/link/TableMerger.cpp +++ b/tools/aapt2/link/TableMerger.cpp @@ -142,7 +142,7 @@ bool TableMerger::doMerge(const Source& src, ResourceTableType* dstType = mMasterPackage->findOrCreateType(srcType->type); if (srcType->symbolStatus.state == SymbolState::kPublic) { if (dstType->symbolStatus.state == SymbolState::kPublic && dstType->id && srcType->id - && dstType->id.value() == srcType->id.value()) { + && dstType->id.value() != srcType->id.value()) { // Both types are public and have different IDs. mContext->getDiagnostics()->error(DiagMessage(src) << "can not merge type '" diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp index 300b56ddb3a1..ff3e21f12d19 100644 --- a/tools/aapt2/link/TableMerger_test.cpp +++ b/tools/aapt2/link/TableMerger_test.cpp @@ -164,6 +164,63 @@ TEST_F(TableMergerTest, OverrideResourceWithOverlay) { EXPECT_EQ(0x0u, foo->value.data); } +TEST_F(TableMergerTest, OverrideSameResourceIdsWithOverlay) { + std::unique_ptr<ResourceTable> base = test::ResourceTableBuilder() + .setPackageId("", 0x7f) + .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), SymbolState::kPublic) + .build(); + std::unique_ptr<ResourceTable> overlay = test::ResourceTableBuilder() + .setPackageId("", 0x7f) + .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), SymbolState::kPublic) + .build(); + + ResourceTable finalTable; + TableMergerOptions tableMergerOptions; + tableMergerOptions.autoAddOverlay = false; + TableMerger merger(mContext.get(), &finalTable, tableMergerOptions); + + ASSERT_TRUE(merger.merge({}, base.get())); + ASSERT_TRUE(merger.mergeOverlay({}, overlay.get())); +} + +TEST_F(TableMergerTest, FailToOverrideConflictingTypeIdsWithOverlay) { + std::unique_ptr<ResourceTable> base = test::ResourceTableBuilder() + .setPackageId("", 0x7f) + .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), SymbolState::kPublic) + .build(); + std::unique_ptr<ResourceTable> overlay = test::ResourceTableBuilder() + .setPackageId("", 0x7f) + .setSymbolState("bool/foo", ResourceId(0x7f, 0x02, 0x0001), SymbolState::kPublic) + .build(); + + ResourceTable finalTable; + TableMergerOptions tableMergerOptions; + tableMergerOptions.autoAddOverlay = false; + TableMerger merger(mContext.get(), &finalTable, tableMergerOptions); + + ASSERT_TRUE(merger.merge({}, base.get())); + ASSERT_FALSE(merger.mergeOverlay({}, overlay.get())); +} + +TEST_F(TableMergerTest, FailToOverrideConflictingEntryIdsWithOverlay) { + std::unique_ptr<ResourceTable> base = test::ResourceTableBuilder() + .setPackageId("", 0x7f) + .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), SymbolState::kPublic) + .build(); + std::unique_ptr<ResourceTable> overlay = test::ResourceTableBuilder() + .setPackageId("", 0x7f) + .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0002), SymbolState::kPublic) + .build(); + + ResourceTable finalTable; + TableMergerOptions tableMergerOptions; + tableMergerOptions.autoAddOverlay = false; + TableMerger merger(mContext.get(), &finalTable, tableMergerOptions); + + ASSERT_TRUE(merger.merge({}, base.get())); + ASSERT_FALSE(merger.mergeOverlay({}, overlay.get())); +} + TEST_F(TableMergerTest, MergeAddResourceFromOverlay) { std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder() .setPackageId("", 0x7f) |