diff options
94 files changed, 2289 insertions, 515 deletions
diff --git a/Android.mk b/Android.mk index 2d5285438392..41966f463661 100644 --- a/Android.mk +++ b/Android.mk @@ -1244,6 +1244,31 @@ LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk include $(BUILD_DROIDDOC) +# ==== generates full navtree for resolving @links in ds postprocessing ==== +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES) +LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES) +LOCAL_STATIC_JAVA_LIBRARIES:=$(framework_docs_LOCAL_STATIC_JAVA_LIBRARIES) +LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES) +LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS) +LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH) +LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR) +LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) + +LOCAL_MODULE := ds-ref-navtree + +LOCAL_DROIDDOC_OPTIONS:= \ + $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \ + -hdf android.whichdoc online \ + -toroot / \ + -atLinksNavtree \ + -navtreeonly + +LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk + +include $(BUILD_DROIDDOC) + # ==== site updates for docs (on the androiddevdocs app engine server) ======================= include $(CLEAR_VARS) diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index eb07fbc5271a..6e38347f7c99 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -1089,7 +1089,7 @@ public class DownloadManager { if (cursor.moveToFirst()) { int status = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_STATUS)); if (DownloadManager.STATUS_SUCCESSFUL == status) { - return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, id); + return ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id); } } } finally { @@ -1425,7 +1425,7 @@ public class DownloadManager { * @hide */ public Uri getDownloadUri(long id) { - return ContentUris.withAppendedId(mBaseUri, id); + return ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id); } /** @@ -1519,7 +1519,7 @@ public class DownloadManager { // return content URI for cache download long downloadId = getLong(getColumnIndex(Downloads.Impl._ID)); - return ContentUris.withAppendedId(mBaseUri, downloadId).toString(); + return ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, downloadId).toString(); } private long getReason(int status) { diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 29ed97e78dcb..981a0551fc7e 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1229,7 +1229,7 @@ public class Notification implements Parcelable getIcon(), title, actionIntent, // safe to alias - new Bundle(mExtras), + mExtras == null ? new Bundle() : new Bundle(mExtras), getRemoteInputs(), getAllowGeneratedReplies()); } diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 189147efa829..6d6dfebced2c 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1167,12 +1167,12 @@ public final class BluetoothDevice implements Parcelable { /** * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. * * @return true confirmation has been sent out * false for error */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPairingConfirmation(boolean confirm) { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot set pairing confirmation"); diff --git a/core/java/android/hardware/camera2/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java index 9478dc002d6c..45fa15e043e5 100644 --- a/core/java/android/hardware/camera2/DngCreator.java +++ b/core/java/android/hardware/camera2/DngCreator.java @@ -27,6 +27,7 @@ import android.location.Location; import android.media.ExifInterface; import android.media.Image; import android.os.SystemClock; +import android.util.Log; import android.util.Size; import java.io.IOException; @@ -89,17 +90,33 @@ public final class DngCreator implements AutoCloseable { throw new IllegalArgumentException("Null argument to DngCreator constructor"); } - // Find current time + // Find current time in milliseconds since 1970 long currentTime = System.currentTimeMillis(); - - // Find boot time - long bootTimeMillis = currentTime - SystemClock.elapsedRealtime(); + // Assume that sensor timestamp has that timebase to start + long timeOffset = 0; + + int timestampSource = characteristics.get( + CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE); + + if (timestampSource == CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME) { + // This means the same timebase as SystemClock.elapsedRealtime(), + // which is CLOCK_BOOTTIME + timeOffset = currentTime - SystemClock.elapsedRealtime(); + } else if (timestampSource == CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN) { + // This means the same timebase as System.currentTimeMillis(), + // which is CLOCK_MONOTONIC + timeOffset = currentTime - SystemClock.uptimeMillis(); + } else { + // Unexpected time source - treat as CLOCK_MONOTONIC + Log.w(TAG, "Sensor timestamp source is unexpected: " + timestampSource); + timeOffset = currentTime - SystemClock.uptimeMillis(); + } // Find capture time (nanos since boot) Long timestamp = metadata.get(CaptureResult.SENSOR_TIMESTAMP); long captureTime = currentTime; if (timestamp != null) { - captureTime = timestamp / 1000000 + bootTimeMillis; + captureTime = timestamp / 1000000 + timeOffset; } // Format for metadata diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 52d6b56609de..b9e9b283106f 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -3198,6 +3198,27 @@ public class ConnectivityManager { } /** + * Informs the system to penalize {@code network}'s score when it becomes unvalidated. This is + * only meaningful if the system is configured not to penalize such networks, e.g., if the + * {@code config_networkAvoidBadWifi} configuration variable is set to 0 and the {@code + * NETWORK_AVOID_BAD_WIFI setting is unset}. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL} + * + * @param network The network to accept. + * + * @hide + */ + public void setAvoidUnvalidated(Network network) { + try { + mService.setAvoidUnvalidated(network); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Resets all connectivity manager settings back to factory defaults. * @hide */ diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index d48c155986f3..4aabda9eb09d 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -161,6 +161,7 @@ interface IConnectivityManager void releaseNetworkRequest(in NetworkRequest networkRequest); void setAcceptUnvalidated(in Network network, boolean accept, boolean always); + void setAvoidUnvalidated(in Network network); int getRestoreDefaultNetworkDelay(int networkType); diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index ce7a1243fe7a..1aed9b322816 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1358,5 +1358,34 @@ public final class PowerManager { + " held=" + mHeld + ", refCount=" + mCount + "}"; } } + + /** + * Wraps a Runnable such that this method immediately acquires the wake lock and then + * once the Runnable is done the wake lock is released. + * + * <p>Example: + * + * <pre> + * mHandler.post(mWakeLock.wrap(() -> { + * // do things on handler, lock is held while we're waiting for this + * // to get scheduled and until the runnable is done executing. + * }); + * </pre> + * + * <p>Note: you must make sure that the Runnable eventually gets executed, otherwise you'll + * leak the wakelock! + * + * @hide + */ + public Runnable wrap(Runnable r) { + acquire(); + return () -> { + try { + r.run(); + } finally { + release(); + } + }; + } } } diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 507379baaa94..90bd11fe83bc 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -724,6 +724,7 @@ public class RecoverySystem { String line = null; int bytesWrittenInMiB = -1, bytesStashedInMiB = -1; int timeTotal = -1; + int uncryptTime = -1; int sourceVersion = -1; while ((line = in.readLine()) != null) { // Here is an example of lines in last_install: @@ -759,6 +760,8 @@ public class RecoverySystem { if (line.startsWith("time")) { timeTotal = scaled; + } else if (line.startsWith("uncrypt_time")) { + uncryptTime = scaled; } else if (line.startsWith("source_build")) { sourceVersion = scaled; } else if (line.startsWith("bytes_written")) { @@ -774,6 +777,9 @@ public class RecoverySystem { if (timeTotal != -1) { MetricsLogger.histogram(context, "ota_time_total", timeTotal); } + if (uncryptTime != -1) { + MetricsLogger.histogram(context, "ota_uncrypt_time", uncryptTime); + } if (sourceVersion != -1) { MetricsLogger.histogram(context, "ota_source_version", sourceVersion); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 1e66c55ada5d..cafdc7193b21 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5929,6 +5929,18 @@ public final class Settings { public static final String DOZE_ENABLED = "doze_enabled"; /** + * Whether the device should pulse on pick up gesture. + * @hide + */ + public static final String DOZE_PULSE_ON_PICK_UP = "doze_pulse_on_pick_up"; + + /** + * Whether the device should pulse on double tap gesture. + * @hide + */ + public static final String DOZE_PULSE_ON_DOUBLE_TAP = "doze_pulse_on_double_tap"; + + /** * The current night mode that has been selected by the user. Owned * and controlled by UiModeManagerService. Constants are as per * UiModeManager. @@ -6413,6 +6425,9 @@ public final class Settings { CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, SYSTEM_NAVIGATION_KEYS_ENABLED, QS_TILES, + DOZE_ENABLED, + DOZE_PULSE_ON_PICK_UP, + DOZE_PULSE_ON_DOUBLE_TAP }; /** @@ -7452,6 +7467,13 @@ public final class Settings { /** * Whether to automatically switch away from wifi networks that lose Internet access. + * Only meaningful if config_networkAvoidBadWifi is set to 0, otherwise the system always + * avoids such networks. Valid values are: + * + * 0: Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013. + * null: Ask the user whether to switch away from bad wifi. + * 1: Avoid bad wifi. + * * @hide */ public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi"; diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 0557d138eb74..e958fbef563d 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -192,6 +192,9 @@ public class DreamService extends Service implements Window.Callback { private boolean mDebug = false; + private PowerManager.WakeLock mWakeLock; + private boolean mWakeLockAcquired; + public DreamService() { mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); } @@ -786,6 +789,8 @@ public class DreamService extends Service implements Window.Callback { public void onCreate() { if (mDebug) Slog.v(TAG, "onCreate()"); super.onCreate(); + mWakeLock = getSystemService(PowerManager.class) + .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DreamService"); } /** @@ -825,9 +830,21 @@ public class DreamService extends Service implements Window.Callback { @Override public final IBinder onBind(Intent intent) { if (mDebug) Slog.v(TAG, "onBind() intent = " + intent); + + // Need to stay awake until we dispatch onDreamingStarted. This is released either in + // attach() or onDestroy(). + mWakeLock.acquire(5000); + mWakeLockAcquired = true; return new DreamServiceWrapper(); } + private void releaseWakeLockIfNeeded() { + if (mWakeLockAcquired) { + mWakeLock.release(); + mWakeLockAcquired = false; + } + } + /** * Stops the dream and detaches from the window. * <p> @@ -904,6 +921,8 @@ public class DreamService extends Service implements Window.Callback { detach(); super.onDestroy(); + + releaseWakeLockIfNeeded(); // for acquire in onBind() } // end public api @@ -944,83 +963,88 @@ public class DreamService extends Service implements Window.Callback { * @param windowToken A window token that will allow a window to be created in the correct layer. */ private final void attach(IBinder windowToken, boolean canDoze) { - if (mWindowToken != null) { - Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken); - return; - } - if (mFinished || mWaking) { - Slog.w(TAG, "attach() called after dream already finished"); - try { - mSandman.finishSelf(windowToken, true /*immediate*/); - } catch (RemoteException ex) { - // system server died + try { + if (mWindowToken != null) { + Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken); + return; + } + if (mFinished || mWaking) { + Slog.w(TAG, "attach() called after dream already finished"); + try { + mSandman.finishSelf(windowToken, true /*immediate*/); + } catch (RemoteException ex) { + // system server died + } + return; } - return; - } - - mWindowToken = windowToken; - mCanDoze = canDoze; - if (mWindowless && !mCanDoze) { - throw new IllegalStateException("Only doze dreams can be windowless"); - } - if (!mWindowless) { - mWindow = new PhoneWindow(this); - mWindow.setCallback(this); - mWindow.requestFeature(Window.FEATURE_NO_TITLE); - mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); - mWindow.setFormat(PixelFormat.OPAQUE); - - if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s", - windowToken, WindowManager.LayoutParams.TYPE_DREAM)); - WindowManager.LayoutParams lp = mWindow.getAttributes(); - lp.type = WindowManager.LayoutParams.TYPE_DREAM; - lp.token = windowToken; - lp.windowAnimations = com.android.internal.R.style.Animation_Dream; - lp.flags |= ( WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR - | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD - | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON - | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0) - | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) - ); - mWindow.setAttributes(lp); - // Workaround: Currently low-profile and in-window system bar backgrounds don't go - // along well. Dreams usually don't need such bars anyways, so disable them by default. - mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - mWindow.setWindowManager(null, windowToken, "dream", true); + mWindowToken = windowToken; + mCanDoze = canDoze; + if (mWindowless && !mCanDoze) { + throw new IllegalStateException("Only doze dreams can be windowless"); + } + if (!mWindowless) { + mWindow = new PhoneWindow(this); + mWindow.setCallback(this); + mWindow.requestFeature(Window.FEATURE_NO_TITLE); + mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); + mWindow.setFormat(PixelFormat.OPAQUE); + + if (mDebug) { + Slog.v(TAG, String.format("Attaching window token: %s to window of type %s", + windowToken, WindowManager.LayoutParams.TYPE_DREAM)); + } - applySystemUiVisibilityFlags( - (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), - View.SYSTEM_UI_FLAG_LOW_PROFILE); + WindowManager.LayoutParams lp = mWindow.getAttributes(); + lp.type = WindowManager.LayoutParams.TYPE_DREAM; + lp.token = windowToken; + lp.windowAnimations = com.android.internal.R.style.Animation_Dream; + lp.flags |= (WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR + | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED + | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD + | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON + | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0) + | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) + ); + mWindow.setAttributes(lp); + // Workaround: Currently low-profile and in-window system bar backgrounds don't go + // along well. Dreams usually don't need such bars anyways, so disable them by default. + mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + mWindow.setWindowManager(null, windowToken, "dream", true); + + applySystemUiVisibilityFlags( + (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), + View.SYSTEM_UI_FLAG_LOW_PROFILE); - try { - getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); - } catch (WindowManager.BadTokenException ex) { - // This can happen because the dream manager service will remove the token - // immediately without necessarily waiting for the dream to start. - // We should receive a finish message soon. - Slog.i(TAG, "attach() called after window token already removed, dream will " - + "finish soon"); - mWindow = null; - return; + try { + getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); + } catch (WindowManager.BadTokenException ex) { + // This can happen because the dream manager service will remove the token + // immediately without necessarily waiting for the dream to start. + // We should receive a finish message soon. + Slog.i(TAG, "attach() called after window token already removed, dream will " + + "finish soon"); + mWindow = null; + return; + } } - } - // We need to defer calling onDreamingStarted until after onWindowAttached, - // which is posted to the handler by addView, so we post onDreamingStarted - // to the handler also. Need to watch out here in case detach occurs before - // this callback is invoked. - mHandler.post(new Runnable() { - @Override - public void run() { + // We need to defer calling onDreamingStarted until after onWindowAttached, + // which is posted to the handler by addView, so we post onDreamingStarted + // to the handler also. Need to watch out here in case detach occurs before + // this callback is invoked. + mHandler.post(mWakeLock.wrap(() -> { if (mWindow != null || mWindowless) { - if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()"); + if (mDebug) { + Slog.v(TAG, "Calling onDreamingStarted()"); + } mStarted = true; onDreamingStarted(); } - } - }); + })); + } finally { + releaseWakeLockIfNeeded(); // for acquire in onBind + } } private boolean getWindowFlagValue(int flag, boolean defaultValue) { diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java index a394f351b7f3..691a385be9da 100644 --- a/core/java/android/view/DragEvent.java +++ b/core/java/android/view/DragEvent.java @@ -134,6 +134,7 @@ public class DragEvent implements Parcelable { Object mLocalState; boolean mDragResult; + boolean mEventHandlerWasCalled; private DragEvent mNext; private RuntimeException mRecycledLocation; @@ -439,6 +440,7 @@ public class DragEvent implements Parcelable { mClipData = null; mClipDescription = null; mLocalState = null; + mEventHandlerWasCalled = false; synchronized (gRecyclerLock) { if (gRecyclerUsed < MAX_RECYCLED) { diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 83feb88fa55d..855b1bce1158 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -331,6 +331,16 @@ interface IWindowManager oneway void statusBarVisibilityChanged(int visibility); /** + * Called by System UI to notify of changes to the visibility of Recents. + */ + oneway void setRecentsVisibility(boolean visible); + + /** + * Called by System UI to notify of changes to the visibility of PIP. + */ + oneway void setTvPipVisibility(boolean visible); + + /** * Device has a software navigation bar (separate from the status bar). */ boolean hasNavigationBar(); diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index f92d83af93a0..9f46f3fc75b0 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -33,6 +33,18 @@ import dalvik.system.CloseGuard; /** * Handle onto a raw buffer that is being managed by the screen compositor. + * + * <p>A Surface is generally created by or from a consumer of image buffers (such as a + * {@link android.graphics.SurfaceTexture}, {@link android.media.MediaRecorder}, or + * {@link android.renderscript.Allocation}), and is handed to some kind of producer (such as + * {@link android.opengl.EGL14#eglCreateWindowSurface(android.opengl.EGLDisplay,android.opengl.EGLConfig,java.lang.Object,int[],int) OpenGL}, + * {@link android.media.MediaPlayer#setSurface MediaPlayer}, or + * {@link android.hardware.camera2.CameraDevice#createCaptureSession CameraDevice}) to draw + * into.</p> + * + * <p><strong>Note:</strong> A Surface acts like a + * {@link java.lang.ref.WeakReference weak reference} to the consumer it is associated with. By + * itself it will not keep its parent consumer from being reclaimed.</p> */ public class Surface implements Parcelable { private static final String TAG = "Surface"; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index a0afeba27fc5..326415211dc1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -40,7 +40,6 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Insets; import android.graphics.Interpolator; import android.graphics.LinearGradient; @@ -831,6 +830,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, protected static boolean sPreserveMarginParamsInLayoutParamConversion; /** + * Prior to N, when drag enters into child of a view that has already received an + * ACTION_DRAG_ENTERED event, the parent doesn't get a ACTION_DRAG_EXITED event. + * ACTION_DRAG_LOCATION and ACTION_DROP were delivered to the parent of a view that returned + * false from its event handler for these events. + * Starting from N, the parent will get ACTION_DRAG_EXITED event before the child gets its + * ACTION_DRAG_ENTERED. ACTION_DRAG_LOCATION and ACTION_DROP are never propagated to the parent. + * sCascadedDragDrop is true for pre-N apps for backwards compatibility implementation. + */ + static boolean sCascadedDragDrop; + + /** * This view does not want keystrokes. Use with TAKES_FOCUS_MASK when * calling setFlags. */ @@ -3086,20 +3096,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * @hide * - * Whether Recents is visible or not. - */ - public static final int RECENT_APPS_VISIBLE = 0x00004000; - - /** - * @hide - * - * Whether the TV's picture-in-picture is visible or not. - */ - public static final int TV_PICTURE_IN_PICTURE_VISIBLE = 0x00010000; - - /** - * @hide - * * Makes navigation bar transparent (but not the status bar). */ public static final int NAVIGATION_BAR_TRANSPARENT = 0x00008000; @@ -4066,6 +4062,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // in apps so we target check it to avoid breaking existing apps. sPreserveMarginParamsInLayoutParamConversion = targetSdkVersion >= N; + sCascadedDragDrop = targetSdkVersion < N; + sCompatibilityDone = true; } } @@ -20859,6 +20857,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return false; } + // Dispatches ACTION_DRAG_ENTERED and ACTION_DRAG_EXITED events for pre-Nougat apps. + boolean dispatchDragEnterExitInPreN(DragEvent event) { + return callDragEventHandler(event); + } + /** * Detects if this View is enabled and has a drag event listener. * If both are true, then it calls the drag event listener with the @@ -20876,13 +20879,44 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * </p> */ public boolean dispatchDragEvent(DragEvent event) { + event.mEventHandlerWasCalled = true; + if (event.mAction == DragEvent.ACTION_DRAG_LOCATION || + event.mAction == DragEvent.ACTION_DROP) { + // About to deliver an event with coordinates to this view. Notify that now this view + // has drag focus. This will send exit/enter events as needed. + getViewRootImpl().setDragFocus(this, event); + } + return callDragEventHandler(event); + } + + final boolean callDragEventHandler(DragEvent event) { + final boolean result; + ListenerInfo li = mListenerInfo; //noinspection SimplifiableIfStatement if (li != null && li.mOnDragListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnDragListener.onDrag(this, event)) { - return true; + result = true; + } else { + result = onDragEvent(event); } - return onDragEvent(event); + + switch (event.mAction) { + case DragEvent.ACTION_DRAG_ENTERED: { + mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED; + refreshDrawableState(); + } break; + case DragEvent.ACTION_DRAG_EXITED: { + mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED; + refreshDrawableState(); + } break; + case DragEvent.ACTION_DRAG_ENDED: { + mPrivateFlags2 &= ~View.DRAG_MASK; + refreshDrawableState(); + } break; + } + + return result; } boolean canAcceptDrag() { diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 6933efc15b68..d4b7d3bdd76a 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -153,8 +153,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ Transformation mInvalidationTransformation; - // View currently under an ongoing drag. Can be null, a child or this window. - private View mCurrentDragView; + // Current frontmost child that can accept drag and lies under the drag location. + // Used only to generate ENTER/EXIT events for pre-Nougat aps. + private View mCurrentDragChild; // Metadata about the ongoing drag private DragEvent mCurrentDragStartEvent; @@ -1355,6 +1356,20 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return mLocalPoint; } + @Override + boolean dispatchDragEnterExitInPreN(DragEvent event) { + if (event.mAction == DragEvent.ACTION_DRAG_EXITED && mCurrentDragChild != null) { + // The drag exited a sub-tree of views; notify of the exit all descendants that are in + // entered state. + // We don't need this recursive delivery for ENTERED events because they get generated + // from the recursive delivery of LOCATION/DROP events, and hence, don't need their own + // recursion. + mCurrentDragChild.dispatchDragEnterExitInPreN(event); + mCurrentDragChild = null; + } + return mIsInterestedInDrag && super.dispatchDragEnterExitInPreN(event); + } + // TODO: Write real docs @Override public boolean dispatchDragEvent(DragEvent event) { @@ -1362,15 +1377,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final float tx = event.mX; final float ty = event.mY; - ViewRootImpl root = getViewRootImpl(); - // Dispatch down the view hierarchy final PointF localPoint = getLocalPoint(); switch (event.mAction) { case DragEvent.ACTION_DRAG_STARTED: { - // clear state to recalculate which views we drag over - mCurrentDragView = null; + // Clear the state to recalculate which views we drag over. + mCurrentDragChild = null; // Set up our tracking of drag-started notifications mCurrentDragStartEvent = DragEvent.obtain(event); @@ -1416,8 +1429,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (child.dispatchDragEvent(event)) { retval = true; } - child.mPrivateFlags2 &= ~View.DRAG_MASK; - child.refreshDrawableState(); } childrenInterestedInDrag.clear(); } @@ -1434,60 +1445,45 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } break; - case DragEvent.ACTION_DRAG_LOCATION: { + case DragEvent.ACTION_DRAG_LOCATION: + case DragEvent.ACTION_DROP: { // Find the [possibly new] drag target View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint); - if (target == null && mIsInterestedInDrag) { - target = this; - } - // If we've changed apparent drag target, tell the view root which view - // we're over now [for purposes of the eventual drag-recipient-changed - // notifications to the framework] and tell the new target that the drag - // has entered its bounds. The root will see setDragFocus() calls all - // the way down to the final leaf view that is handling the LOCATION event - // before reporting the new potential recipient to the framework. - if (mCurrentDragView != target) { - root.setDragFocus(target); - - final int action = event.mAction; - // Position should not be available for ACTION_DRAG_ENTERED and ACTION_DRAG_EXITED. - event.mX = 0; - event.mY = 0; - - // If we've dragged off of a child view or this window, send it the EXITED message - if (mCurrentDragView != null) { - final View view = mCurrentDragView; - event.mAction = DragEvent.ACTION_DRAG_EXITED; - if (view != this) { - view.dispatchDragEvent(event); - view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED; - view.refreshDrawableState(); - } else { - super.dispatchDragEvent(event); + if (target != mCurrentDragChild) { + if (sCascadedDragDrop) { + // For pre-Nougat apps, make sure that the whole hierarchy of views that contain + // the drag location is kept in the state between ENTERED and EXITED events. + // (Starting with N, only the innermost view will be in that state). + + final int action = event.mAction; + // Position should not be available for ACTION_DRAG_ENTERED and + // ACTION_DRAG_EXITED. + event.mX = 0; + event.mY = 0; + + if (mCurrentDragChild != null) { + event.mAction = DragEvent.ACTION_DRAG_EXITED; + mCurrentDragChild.dispatchDragEnterExitInPreN(event); } - } - - mCurrentDragView = target; - // If we've dragged over a new child view, send it the ENTERED message, otherwise - // send it to this window. - if (target != null) { - event.mAction = DragEvent.ACTION_DRAG_ENTERED; - if (target != this) { - target.dispatchDragEvent(event); - target.mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED; - target.refreshDrawableState(); - } else { - super.dispatchDragEvent(event); + if (target != null) { + event.mAction = DragEvent.ACTION_DRAG_ENTERED; + target.dispatchDragEnterExitInPreN(event); } + + event.mAction = action; + event.mX = tx; + event.mY = ty; } - event.mAction = action; // restore the event's original state - event.mX = tx; - event.mY = ty; + mCurrentDragChild = target; } - // Dispatch the actual drag location notice, localized into its coordinates + if (target == null && mIsInterestedInDrag) { + target = this; + } + + // Dispatch the actual drag notice, localized into the target coordinates. if (target != null) { if (target != this) { event.mX = localPoint.x; @@ -1497,55 +1493,21 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager event.mX = tx; event.mY = ty; - } else { - retval = super.dispatchDragEvent(event); - } - } - } break; - /* Entered / exited dispatch - * - * DRAG_ENTERED is not dispatched downwards from ViewGroup. The reason for this is - * that we're about to get the corresponding LOCATION event, which we will use to - * determine which of our children is the new target; at that point we will - * push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup]. - * If no suitable child is detected, dispatch to this window. - * - * DRAG_EXITED *is* dispatched all the way down immediately: once we know the - * drag has left this ViewGroup, we know by definition that every contained subview - * is also no longer under the drag point. - */ + if (mIsInterestedInDrag) { + final boolean eventWasConsumed; + if (sCascadedDragDrop) { + eventWasConsumed = retval; + } else { + eventWasConsumed = event.mEventHandlerWasCalled; + } - case DragEvent.ACTION_DRAG_EXITED: { - if (mCurrentDragView != null) { - final View view = mCurrentDragView; - if (view != this) { - view.dispatchDragEvent(event); - view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED; - view.refreshDrawableState(); + if (!eventWasConsumed) { + retval = super.dispatchDragEvent(event); + } + } } else { - super.dispatchDragEvent(event); - } - - mCurrentDragView = null; - } - } break; - - case DragEvent.ACTION_DROP: { - if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event); - View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint); - if (target != null) { - if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, " dispatch drop to " + target); - event.mX = localPoint.x; - event.mY = localPoint.y; - retval = target.dispatchDragEvent(event); - event.mX = tx; - event.mY = ty; - } else if (mIsInterestedInDrag) { - retval = super.dispatchDragEvent(event); - } else { - if (ViewDebug.DEBUG_DRAG) { - Log.d(View.VIEW_LOG_TAG, " not dropped on an accepting view"); + retval = super.dispatchDragEvent(event); } } } break; @@ -1592,6 +1554,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final boolean canAccept = child.dispatchDragEvent(mCurrentDragStartEvent); mCurrentDragStartEvent.mX = tx; mCurrentDragStartEvent.mY = ty; + mCurrentDragStartEvent.mEventHandlerWasCalled = false; if (canAccept) { mChildrenInterestedInDrag.add(child); if (!child.canAcceptDrag()) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 1d541f6c0c4c..e95fa5e6a2ee 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -5523,9 +5523,11 @@ public final class ViewRootImpl implements ViewParent, if (what == DragEvent.ACTION_DRAG_EXITED) { // A direct EXITED event means that the window manager knows we've just crossed // a window boundary, so the current drag target within this one must have - // just been exited. Send it the usual notifications and then we're done - // for now. - mView.dispatchDragEvent(event); + // just been exited. Send the EXITED notification to the current drag view, if any. + if (View.sCascadedDragDrop) { + mView.dispatchDragEnterExitInPreN(event); + } + setDragFocus(null, event); } else { // For events with a [screen] location, translate into window coordinates if ((what == DragEvent.ACTION_DRAG_LOCATION) || (what == DragEvent.ACTION_DROP)) { @@ -5548,6 +5550,12 @@ public final class ViewRootImpl implements ViewParent, // Now dispatch the drag/drop event boolean result = mView.dispatchDragEvent(event); + if (what == DragEvent.ACTION_DRAG_LOCATION && !event.mEventHandlerWasCalled) { + // If the LOCATION event wasn't delivered to any handler, no view now has a drag + // focus. + setDragFocus(null, event); + } + // If we changed apparent drag target, tell the OS about it if (prevDragView != mCurrentDragView) { try { @@ -5575,6 +5583,7 @@ public final class ViewRootImpl implements ViewParent, // When the drag operation ends, reset drag-related state if (what == DragEvent.ACTION_DRAG_ENDED) { + mCurrentDragView = null; setLocalDragState(null); mAttachInfo.mDragToken = null; if (mAttachInfo.mDragSurface != null) { @@ -5634,10 +5643,33 @@ public final class ViewRootImpl implements ViewParent, return mLastTouchSource; } - public void setDragFocus(View newDragTarget) { - if (mCurrentDragView != newDragTarget) { - mCurrentDragView = newDragTarget; + public void setDragFocus(View newDragTarget, DragEvent event) { + if (mCurrentDragView != newDragTarget && !View.sCascadedDragDrop) { + // Send EXITED and ENTERED notifications to the old and new drag focus views. + + final float tx = event.mX; + final float ty = event.mY; + final int action = event.mAction; + // Position should not be available for ACTION_DRAG_ENTERED and ACTION_DRAG_EXITED. + event.mX = 0; + event.mY = 0; + + if (mCurrentDragView != null) { + event.mAction = DragEvent.ACTION_DRAG_EXITED; + mCurrentDragView.callDragEventHandler(event); + } + + if (newDragTarget != null) { + event.mAction = DragEvent.ACTION_DRAG_ENTERED; + newDragTarget.callDragEventHandler(event); + } + + event.mAction = action; + event.mX = tx; + event.mY = ty; } + + mCurrentDragView = newDragTarget; } private AudioManager getAudioManager() { diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 2b3d6436dd4e..46a0194b1b09 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -1306,6 +1306,16 @@ public interface WindowManagerPolicy { public int adjustSystemUiVisibilityLw(int visibility); /** + * Called by System UI to notify of changes to the visibility of Recents. + */ + public void setRecentsVisibilityLw(boolean visible); + + /** + * Called by System UI to notify of changes to the visibility of PIP. + */ + public void setTvPipVisibilityLw(boolean visible); + + /** * Specifies whether there is an on-screen navigation bar separate from the status bar. */ public boolean hasNavigationBar(); diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml index 7e7c2de04b68..3458bdc73149 100644 --- a/docs/html/_redirects.yaml +++ b/docs/html/_redirects.yaml @@ -1282,3 +1282,6 @@ redirects: - from: /preview/features/data-saver.html to: /training/basics/network-ops/data-saver.html +# Temporary redirect +- from: /ndk/guides/cmake.html + to: https://sites.google.com/a/android.com/tools/tech-docs/external-c-builds#TOC-CMake-Variables-List
\ No newline at end of file diff --git a/docs/html/develop/index.jd b/docs/html/develop/index.jd index bd933f4f2f76..b1d738832b3a 100644 --- a/docs/html/develop/index.jd +++ b/docs/html/develop/index.jd @@ -14,24 +14,26 @@ excludeFromSuggestions=true <div class="wrap"> <div class="cols dac-hero-content"> <div class="col-1of2 col-push-1of2 dac-hero-figure"> - <img class="dac-hero-image" src="/images/develop/hero_image_studio5_2x.png" srcset="/images/develop/hero_image_studio5.png 1x, /images/develop/hero_image_studio5_2x.png 2x"> + <img class="dac-hero-image" style="padding-top:32px" + src="/images/develop/hero-layout-editor_2x.png"> </div> <div class="col-1of2 col-pull-1of2" style="margin-bottom:40px"> <h1 class="dac-hero-title"> <a style="color:inherit" href="{@docRoot}studio/index.html"> - Android Studio 2.1,<br>now available!</a></h1> + Android Studio 2.2 <nobr>is here!</nobr></a></h1> -<p class="dac-hero-description">Android Studio provides the fastest tools for -building apps on every type of Android device.</p> +<p class="dac-hero-description">The latest version of Android Studio includes a +rewritten <b>layout editor</b> with the new constraint layout, +helping you build rich UI with less work.</p> -<p class="dac-hero-description">The latest version, Android Studio 2.1, adds -support for N Preview development on top of the faster Android Emulator and -Instant Run feature from 2.0.</p> + +<p class="dac-hero-description">With over 20 new features, Android Studio +2.2 helps you code faster and smarter.</p> <p style="margin-top:24px"> <a class="dac-hero-cta" href="{@docRoot}studio/index.html"> <span class="dac-sprite dac-auto-chevron"></span> - Get Android Studio + Get Android Studio 2.2 </a> <wbr> <a class="dac-hero-cta" href="{@docRoot}studio/releases/index.html"> diff --git a/docs/html/google/play/billing/billing_admin.jd b/docs/html/google/play/billing/billing_admin.jd index 292cfcce0c36..ad09f1f53b40 100644 --- a/docs/html/google/play/billing/billing_admin.jd +++ b/docs/html/google/play/billing/billing_admin.jd @@ -235,7 +235,7 @@ separate subvalues. For example, the syntax for the CSV file is as follows:</p> <p>"<em>product_id</em>","<em>publish_state</em>","<em>purchase_type</em>","<em>autotranslate</em> ","<em>locale</em>; <em>title</em>; <em>description</em>","<em>autofill</em>","<em>country</em>; -<em>price</em>" +<em>price</em>", "<em>pricing_template_id</em>" </p> <p>Descriptions and usage details are provided below.</p> @@ -316,8 +316,9 @@ separate subvalues. For example, the syntax for the CSV file is as follows:</p> <p>"true","<em>default_price_in_home_currency</em>" </li> <li> - <p>If <em>autofill</em> is set to <code>false</code>, you need to specify a <em>country</em> - and a <em>price</em> for each currency, and you must use the following syntax:</p> + <p>If <em>autofill</em> is set to <code>false</code>, you need to either specify the <em>pricing_template_id</em> + that is linked to the in-app product or specify a <em>country</em> and a <em>price</em> for each currency. + If you choose to specify countries and prices, you must use the following syntax:</p> <p>"false", "<em>home_country</em>; <em>default_price_in_home_currency</em>; <em>country_2</em>; <em>country_2_price</em>; <em>country_3</em>; <em>country_3_price</em>; ..."</p> </li> @@ -335,11 +336,41 @@ separate subvalues. For example, the syntax for the CSV file is as follows:</p> </dd> <dt>price</dt> <dd> + <p> + If you use this value, you shouldn't specify a value for the <em>pricing_template_id</em>. + </p> + <p> This is equivalent to the Price in the In-app Products UI. The price must be specified in micro-units. To convert a currency value to micro-units, you multiply the real value by 1,000,000. For example, if you want to sell an in-app item for $1.99, you specify <code>1990000</code> in the <em>price</em> field. + </p> + </dd> + <dt>pricing_template_id</dt> + <dd> + <p> + If you use this value, you should set <em>autofill</em> to + <code>false</code> and leave the <em>price</em> column empty. + </p> + <p> + This value represents the ID of the pricing template that you've linked to + the in-app product. This ID appears under a pricing template's name + on the <strong>Pricing template</strong> page. If an in-app product isn't + linked to a pricing template, its <em>pricing_template_id</em> value is + empty. + </p> + <p> + If you import a CSV file and choose to overwrite the product list, you can + update the links between in-app products and pricing templates by changing + the value of an in-app product's <em>pricing_template_id</em>. Leave the + value empty to unlink an in-app product from all pricing templates. + </p> + <p> + <strong>Note: </strong>You can link up to 100 app prices or in-app product + prices with a particular pricing template. Therefore, don't specify the same + <em>pricing_template_id</em> value in more than 100 rows of your CSV file. + </p> </dd> </dl> @@ -432,8 +463,11 @@ with the <em>locale</em> field.</p> <p> When creating a pricing template, you provide new pricing information that you - can apply to paid apps and in-app products. To add a pricing template, do the - following: + can apply to paid apps and in-app products. You can link the prices of up to + 100 apps and in-app products to a single pricing template. +</p> +</p> + To add a pricing template, do the following: </p> <ol> diff --git a/docs/html/guide/topics/location/strategies.jd b/docs/html/guide/topics/location/strategies.jd index eb436d0138a4..548ed9c99f34 100755 --- a/docs/html/guide/topics/location/strategies.jd +++ b/docs/html/guide/topics/location/strategies.jd @@ -160,22 +160,25 @@ android.location.LocationManager#GPS_PROVIDER}.</p> android.location.LocationManager#NETWORK_PROVIDER} and {@link android.location.LocationManager#GPS_PROVIDER}, then you need to request only the {@code ACCESS_FINE_LOCATION} permission, because it includes permission - for both providers. (Permission for {@code ACCESS_COARSE_LOCATION} includes - permission only for {@link - android.location.LocationManager#NETWORK_PROVIDER}.) + for both providers. Permission for {@code ACCESS_COARSE_LOCATION} allows + access only to {@link android.location.LocationManager#NETWORK_PROVIDER}. </p> -<p class="note"> - <strong>Note:</strong> If your app targets Android 5.0 (API level 21) or - higher, you must also declare that your app uses the +<p id="location-feature-caution" class="caution"> + <strong>Caution:</strong> If your app targets Android 5.0 (API level 21) or + higher, you <em>must</em> declare that your app uses the <code>android.hardware.location.network</code> or <code>android.hardware.location.gps</code> hardware feature in the manifest file, depending on whether your app receives location updates from {@link android.location.LocationManager#NETWORK_PROVIDER} or from {@link android.location.LocationManager#GPS_PROVIDER}. If your app receives location - information from both of these providers, you need to declare that the app - uses both <code>android.hardware.location.network</code> and - <code>android.hardware.location.gps</code>. + information from either of these location provider sources, you need to + declare that the app uses these hardware features in your app manifest. + On devices running verions prior to Android 5.0 (API 21), requesting the + {@code ACCESS_FINE_LOCATION} or {@code ACCESS_COARSE_LOCATION} permission + includes an implied request for location hardware features. However, + requesting those permissions <em>does not</em> automatically request location + hardware features on Android 5.0 (API level 21) and higher. </p> <p> diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd index aaa5867cf4f5..26ae59f9d336 100755 --- a/docs/html/guide/topics/manifest/uses-feature-element.jd +++ b/docs/html/guide/topics/manifest/uses-feature-element.jd @@ -1666,13 +1666,13 @@ densities: '160' <pre><uses-feature android:name="android.hardware.camera" android:required="false" /></pre> -<p class="note"> - <strong>Note:</strong> If your app targets Android 5.0 (API level 21) or +<p class="caution"> + <strong>Caution:</strong> If your app targets Android 5.0 (API level 21) or higher and uses the <code>ACCESS_COARSE_LOCATION</code> or <code>ACCESS_FINE_LOCATION</code> permission in order to receive location updates from the network or a GPS, respectively, you must also explicitly declare that your app uses the <code>android.hardware.location.network</code> - or <code>android.hardware.location.gps</code> hardware feature, respectively. + or <code>android.hardware.location.gps</code> hardware features. </p> <p class="table-caption" id="permissions-features"> @@ -1731,8 +1731,8 @@ densities: '160' <code>android.hardware.location</code> </p> <p> - <code>android.hardware.location.network</code> (Target API level 20 or - lower only.) + <code>android.hardware.location.network</code> + (Only when target API level is 20 orlower.) </p> </td> <!-- <td></td> --> @@ -1744,8 +1744,8 @@ densities: '160' <code>android.hardware.location</code> </p> <p> - <code>android.hardware.location.gps</code> (Target API level 20 or lower - only.) + <code>android.hardware.location.gps</code> + (Only when target API level is 20 orlower.) </p> </td> diff --git a/docs/html/guide/topics/media/camera.jd b/docs/html/guide/topics/media/camera.jd index fcf1ab1fc9db..383b6c1f32ca 100644 --- a/docs/html/guide/topics/media/camera.jd +++ b/docs/html/guide/topics/media/camera.jd @@ -156,7 +156,7 @@ application must request the audio capture permission. </li> <li> <p><strong>Location Permission</strong> - If your application tags images - with GPS location information, you must request the "fine location" + with GPS location information, you must request the {@code ACCESS_FINE_LOCATION} permission. Note that, if your app targets Android 5.0 (API level 21) or higher, you also need to declare that your app uses the device's GPS:</p> <pre> diff --git a/docs/html/guide/topics/resources/multilingual-support.jd b/docs/html/guide/topics/resources/multilingual-support.jd index 8d8484b262e9..28699fe01a06 100644 --- a/docs/html/guide/topics/resources/multilingual-support.jd +++ b/docs/html/guide/topics/resources/multilingual-support.jd @@ -88,15 +88,17 @@ today.</p> <h2 id="postN">Improvements to Resource-Resolution Strategy</h2> <p>Android 7.0 (API level 24) brings more robust resource resolution, and - finds better fallbacks automatically. However, to speed up resolution and - improve + finds better fallbacks automatically. + However, to speed up resolution and improve maintainability, you should store resources in the most common parent dialect. - For example, if you were storing Spanish resources in the {@code es-US} - directory - before, move them into the {@code es-419} directory, which contains Latin - American Spanish. - Similarly, if you have resource strings in a folder named {@code en-GB}, rename - the folder to {@code en-001} (international English), because the most common + For example, if you were storing Spanish resources + in the {@code values-es-rUS} directory + before, move them into the {@code values-b+es+419} directory, + which contains Latin American Spanish. + Similarly, if you have resource strings in a + directory named {@code values-en-rGB}, rename + the directory to {@code values-b+en+001} (International + English), because the most common parent for <code>en-GB</code> strings is {@code en-001}. The following examples explain why these practices improve performance and reliability of resource resolution.</p> diff --git a/docs/html/images/develop/hero-layout-editor_2x.png b/docs/html/images/develop/hero-layout-editor_2x.png Binary files differnew file mode 100644 index 000000000000..56dfbf38719b --- /dev/null +++ b/docs/html/images/develop/hero-layout-editor_2x.png diff --git a/docs/html/index.jd b/docs/html/index.jd index fe5dadaa45a5..39e3d73439ee 100644 --- a/docs/html/index.jd +++ b/docs/html/index.jd @@ -63,6 +63,40 @@ nonavpage=true </div><!-- end .wrap --> </div><!-- end .dac-actions --> + +<section class="dac-expand dac-hero" style="background-color:#FFF0B4;"> + <div class="wrap" style="max-width:1000px;margin-top:0;overflow:auto"> + <div class="col-7of16 col-push-8of16 dac-hero-figure"> + <a href="/studio/index.html"> + <img class="dac-hero-image" style="padding-top:24px" + src="/studio/images/hero_image_studio_2-2_2x.png"> + </a> + </div> + <div class="col-7of16 col-pull-6of16"> + <h1 class="dac-hero-title" style="color:#004d40">Android Studio 2.2!</h1> +<p class="dac-hero-description" style="color:#004d40">The latest update is +packed with over 20 new features, like a rewritten layout editor with the +new constraint layout, support for Android 7.0 Nougat, Espresso test recording, +enhanced Jack compiler / Java 8 support, expanded C++ support with CMake and +NDK-Build, and much more!</p> +<p class="dac-hero-description" style="color:#004d40">Android Studio 2.2 +helps you code faster and smarter.</p> + +<p style="margin-top:24px"> + <a class="dac-hero-cta" href="/studio/index.html" style="color:#004d40"> + <span class="dac-sprite dac-auto-chevron"></span> + Get Android Studio 2.2 + </a> + <wbr> + <a class="dac-hero-cta" href="/studio/releases/index.html" style="color:#004d40"> + <span class="dac-sprite dac-auto-chevron"></span> + See the release notes</a> +</p> + </div> + </div> +</section> + + <section class="dac-section dac-light" id="build-apps"><div class="wrap"> <h1 class="dac-section-title">Build Beautiful Apps</h1> <div class="dac-section-subtitle"> diff --git a/docs/html/jd_extras_en.js b/docs/html/jd_extras_en.js index f3469b4f4ec1..e4bd36856c9f 100644 --- a/docs/html/jd_extras_en.js +++ b/docs/html/jd_extras_en.js @@ -4127,8 +4127,8 @@ METADATA['en'].collections = { "develop/landing/tools": { "title": "", "resources": [ - "https://www.youtube.com/watch?v=ZOz_yr8Yxq8&list=PLWz5rJ2EKKc_w6fodMGrA1_tsI3pqPbqa", - "https://www.youtube.com/watch?v=eOV2owswDkE&list=PLWz5rJ2EKKc_w6fodMGrA1_tsI3pqPbqa", + "https://www.youtube.com/watch?v=NbHsi3-uR8E&list=PLWz5rJ2EKKc_w6fodMGrA1_tsI3pqPbqa", + "https://www.youtube.com/watch?v=-SY5nkNVUn0&list=PLWz5rJ2EKKc_w6fodMGrA1_tsI3pqPbqa", "https://www.youtube.com/watch?v=StqAZ1OQbqA&list=PLWz5rJ2EKKc_w6fodMGrA1_tsI3pqPbqa", "https://www.youtube.com/watch?v=-SY5nkNVUn0&list=PLWz5rJ2EKKc_w6fodMGrA1_tsI3pqPbqa", "https://www.youtube.com/watch?v=4rI4tTd7-J8&list=PLWz5rJ2EKKc_w6fodMGrA1_tsI3pqPbqa", @@ -5568,7 +5568,7 @@ METADATA['en'].collections = { "title": "", "resources": [ "https://medium.com/google-developers/how-often-should-you-update-android-studio-db25785c488e#.8blbql35x", - "http://android-developers.blogspot.com/2016/04/android-studio-2-0.html", + "https://android-developers.blogspot.com/2016/09/android-studio-2-2.html", "https://medium.com/google-developers/writing-more-code-by-writing-less-code-with-android-studio-live-templates-244f648d17c7#.hczcm02du", ] }, diff --git a/docs/html/topic/arc/index.jd b/docs/html/topic/arc/index.jd index d46fbc89ac59..a025459226aa 100644 --- a/docs/html/topic/arc/index.jd +++ b/docs/html/topic/arc/index.jd @@ -51,7 +51,7 @@ review your mouse and keyboard interactions. <!-- Some Chromebooks don't support touch. Although not essential, it's a good idea to explicitly include this declaration. --> <uses-feature android:name="android.hardware.touchscreen" - required="false" /> + android:required="false" /> </manifest> </pre> diff --git a/docs/html/training/_book.yaml b/docs/html/training/_book.yaml index c4551d5229ab..e9635be74ec3 100644 --- a/docs/html/training/_book.yaml +++ b/docs/html/training/_book.yaml @@ -900,6 +900,11 @@ toc: value: 順応性のある UI フローの実装 - name: zh-cn-lang value: 实施自适应用户界面流程 + - title: Build a Responsive UI with ConstraintLayout + path: /training/constraint-layout/index.html + path_attributes: + - name: description + value: How to build a layout using ConstraintLayout and the Android Studio Layout Editor. - title: Adding the App Bar path: /training/appbar/index.html path_attributes: @@ -1151,6 +1156,8 @@ toc: value: 维护兼容性 - name: zh-tw-lang value: 維持相容性 + - title: Selecting Colors with the Palette API + path: /training/material/palette-colors.html - title: Best Practices for User Input path: /training/best-user-input.html diff --git a/docs/html/training/constraint-layout/images/alignment-constraint-offset_2x.png b/docs/html/training/constraint-layout/images/alignment-constraint-offset_2x.png Binary files differnew file mode 100644 index 000000000000..1e4867e6513a --- /dev/null +++ b/docs/html/training/constraint-layout/images/alignment-constraint-offset_2x.png diff --git a/docs/html/training/constraint-layout/images/alignment-constraint_2x.png b/docs/html/training/constraint-layout/images/alignment-constraint_2x.png Binary files differnew file mode 100644 index 000000000000..afe7d4aed282 --- /dev/null +++ b/docs/html/training/constraint-layout/images/alignment-constraint_2x.png diff --git a/docs/html/training/constraint-layout/images/baseline-constraint_2x.png b/docs/html/training/constraint-layout/images/baseline-constraint_2x.png Binary files differnew file mode 100644 index 000000000000..dfc35226fdb5 --- /dev/null +++ b/docs/html/training/constraint-layout/images/baseline-constraint_2x.png diff --git a/docs/html/training/constraint-layout/images/constraint-fail-fixed_2x.png b/docs/html/training/constraint-layout/images/constraint-fail-fixed_2x.png Binary files differnew file mode 100644 index 000000000000..be9d54f4fbf5 --- /dev/null +++ b/docs/html/training/constraint-layout/images/constraint-fail-fixed_2x.png diff --git a/docs/html/training/constraint-layout/images/constraint-fail_2x.png b/docs/html/training/constraint-layout/images/constraint-fail_2x.png Binary files differnew file mode 100644 index 000000000000..3f28ef7906ab --- /dev/null +++ b/docs/html/training/constraint-layout/images/constraint-fail_2x.png diff --git a/docs/html/training/constraint-layout/images/layout-editor-convert-to-constraint_2x.png b/docs/html/training/constraint-layout/images/layout-editor-convert-to-constraint_2x.png Binary files differnew file mode 100644 index 000000000000..ace31a6105d9 --- /dev/null +++ b/docs/html/training/constraint-layout/images/layout-editor-convert-to-constraint_2x.png diff --git a/docs/html/training/constraint-layout/images/layout-editor-margin-callout_2-2_2x.png b/docs/html/training/constraint-layout/images/layout-editor-margin-callout_2-2_2x.png Binary files differnew file mode 100644 index 000000000000..07680227fbb7 --- /dev/null +++ b/docs/html/training/constraint-layout/images/layout-editor-margin-callout_2-2_2x.png diff --git a/docs/html/training/constraint-layout/images/layout-editor-properties-callouts_2-2_2x.png b/docs/html/training/constraint-layout/images/layout-editor-properties-callouts_2-2_2x.png Binary files differnew file mode 100644 index 000000000000..b4ffb2cd946a --- /dev/null +++ b/docs/html/training/constraint-layout/images/layout-editor-properties-callouts_2-2_2x.png diff --git a/docs/html/training/constraint-layout/images/layout-editor_2-2_2x.png b/docs/html/training/constraint-layout/images/layout-editor_2-2_2x.png Binary files differnew file mode 100644 index 000000000000..72a4e401a05c --- /dev/null +++ b/docs/html/training/constraint-layout/images/layout-editor_2-2_2x.png diff --git a/docs/html/training/constraint-layout/images/parent-constraint_2x.png b/docs/html/training/constraint-layout/images/parent-constraint_2x.png Binary files differnew file mode 100644 index 000000000000..0414f1d5b34b --- /dev/null +++ b/docs/html/training/constraint-layout/images/parent-constraint_2x.png diff --git a/docs/html/training/constraint-layout/images/position-constraint_2x.png b/docs/html/training/constraint-layout/images/position-constraint_2x.png Binary files differnew file mode 100644 index 000000000000..9f93e72dcd4d --- /dev/null +++ b/docs/html/training/constraint-layout/images/position-constraint_2x.png diff --git a/docs/html/training/constraint-layout/images/thumbnail-add-layout-guideline_2-2.png b/docs/html/training/constraint-layout/images/thumbnail-add-layout-guideline_2-2.png Binary files differnew file mode 100644 index 000000000000..f863e5f99bd1 --- /dev/null +++ b/docs/html/training/constraint-layout/images/thumbnail-add-layout-guideline_2-2.png diff --git a/docs/html/training/constraint-layout/images/thumbnail-adjust-constraint-bias.png b/docs/html/training/constraint-layout/images/thumbnail-adjust-constraint-bias.png Binary files differnew file mode 100644 index 000000000000..d61e9b2354f1 --- /dev/null +++ b/docs/html/training/constraint-layout/images/thumbnail-adjust-constraint-bias.png diff --git a/docs/html/training/constraint-layout/images/thumbnail-studio-constraint-first.png b/docs/html/training/constraint-layout/images/thumbnail-studio-constraint-first.png Binary files differnew file mode 100644 index 000000000000..97471025b007 --- /dev/null +++ b/docs/html/training/constraint-layout/images/thumbnail-studio-constraint-first.png diff --git a/docs/html/training/constraint-layout/images/thumbnail-studio-constraint-second.png b/docs/html/training/constraint-layout/images/thumbnail-studio-constraint-second.png Binary files differnew file mode 100644 index 000000000000..940b84955278 --- /dev/null +++ b/docs/html/training/constraint-layout/images/thumbnail-studio-constraint-second.png diff --git a/docs/html/training/constraint-layout/index.html b/docs/html/training/constraint-layout/index.html new file mode 100644 index 000000000000..62eaf15f62a7 --- /dev/null +++ b/docs/html/training/constraint-layout/index.html @@ -0,0 +1,498 @@ +<html devsite> +<head> + <title>Build a Responsive UI with ConstraintLayout</title> + <meta name="book_path" value="/training/_book.yaml" /> + <meta name="top_category" value="develop" /> + <meta name="subcategory" value="training" /> +</head> +<body> + +<div id="tb-wrapper"> +<div id="tb"> + <h2>In this document</h2> + <ol> + <li><a href="#constraints-overview">Constraints overview</a></li> + <li><a href="#add-constraintlayout-to-your-project">Add ConstraintLayout to your project</a></li> + <li><a href="#add-a-constraint">Add a constraint</a></li> + <li><a href="#use-autoconnect-and-infer-constraints">Use Autoconnect and Infer Constraints</a></li> + <li><a href="#adjust-the-view-size">Adjust the view size</a></li> + <li><a href="#adjust-the-constraint-bias">Adjust the constraint bias</a></li> + <li><a href="#adjust-the-view-margins">Adjust the view margins</a></li> + </ol> +</div> +</div> + + +<p><code>ConstraintLayout</code> allows you to create large and complex layouts with a flat view +hierarchy (no nested view groups). It's similar to <code>RelativeLayout</code> in that all views are +layed out according to relationships between sibling views and the parent layout, but it's more +flexible than <code>RelativeLayout</code> and easier to use with Android Studio's Layout Editor. +</p> + +<p>Everything you can do with <code>ConstraintLayout</code> is available directly from the Layout Editor's visual +tools, because the layout API and the Layout Editor were specially built for each other. So you can +build your layout with <code>ConstraintLayout</code> entirely by drag-and-dropping instead of editing the XML. +</p> + +<img src="/training/constraint-layout/images/layout-editor_2-2_2x.png" alt="" + width="640"/> +<p class="img-caption"><b>Figure 1.</b> A <code>ConstraintLayout</code> in the Layout Editor</p> + + +<p> +<code>ConstraintLayout</code> is available in an API library that's compatible with Android +2.3 (API level 9) and higher, and the new layout editor is available in Android +Studio 2.2 and higher. +</p> + +<p> +This page provides a guide to building a layout with <code>ConstraintLayout</code> in Android +Studio. If you'd like more information about the Layout Editor itself, see the +Android Studio guide to <a href="/studio/write/layout-editor.html">Build a UI with +Layout Editor</a>. +</p> + + +<h2 id="constraints-overview">Constraints overview</h2> +<p> +To define a view's position in <code>ConstraintLayout</code>, you must add two +or more <em>constraints</em> for the view. Each constraint represents a connection or +alignment to another view, the parent layout, or an invisible guideline. Each +constraint defines the view's position along either the +vertical or horizontal axis; so each view must have a minimum of one constraint for each +axis, but often more are necessary. +</p> + +<p> +When you drop a view into the Layout Editor, it stays where you leave it even if +it has no constraints. However, this is only to make editing easier; if a view has +no constraints when you run your layout on a device, it is drawn at +position [0,0] (the top-left corner).</p> + +<p>In figure 2, the layout looks good in the +editor, but there's no vertical constraint on <code>TextView B</code>. When this +layout draws on a device, <code>TextView B</code> horizontally aligns with the left and +right edges of the <code>ImageView</code>, but appears at the top of the screen because +it has no vertical constraint. +</p> + +<div class="cols"> +<div class="col-1of2"> +<img src="/training/constraint-layout/images/constraint-fail_2x.png" width="100%" alt="" /> +<p class="img-caption"><strong>Figure 2.</strong> <code>TextView B</code> is missing a +vertical constraint</p> +</div> +<div class="col-1of2"> +<img src="/training/constraint-layout/images/constraint-fail-fixed_2x.png" width="100%" alt="" /> +<p class="img-caption"><strong>Figure 3.</strong> <code>TextView B</code> is now vertically +constrained to the <code>ImageView</code></p> +</div> +</div> + +<p> +Although a missing constraint won't cause a compilation error, the Layout Editor +indicates missing constraints as an error in the toolbar. To view the errors and +other warnings, click <strong>Show Warnings and Errors</strong> +<img src="/studio/images/buttons/layout-editor-errors.png" class="inline-icon" alt="" />. +To help you avoid missing constraints, the Layout Editor can automatically add +constraints for you with the <a +href="#use-autoconnect-and-infer-constraints">Autoconnect and infer +constraints</a> features. +</p> + + +<h2 id="add-constraintlayout-to-your-project">Add ConstraintLayout to your project</h2> +<p> +To use <code>ConstraintLayout</code> in your project, proceed as follows: +</p> + +<ol> +<li>Ensure you have the latest Constraint Layout library: +<ol> + <li>Click <strong>Tools > Android > SDK Manager</strong>. + <li>Click the <strong>SDK Tools</strong> tab. + <li>Expand <strong>Support Repository</strong> and then check +<b>ConstraintLayout for Android</b> and <b>Solver for ConstraintLayout</b>. +Check <b>Show Package Details</b> and take note of the version you're downloading +(you'll need this below).</p> + <li>Click <strong>OK</strong>. +<li>Add the ConstraintLayout library as a dependency in your module-level + <code>build.gradle</code> file: +<pre> +dependencies { + compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha8' +} +</pre> + <p>The library version you download may be higher, so be sure the value you specify + here matches the version from step 3.</p> +</li> +<li>In the toolbar or sync notification, click <strong>Sync Project with Gradle +Files</strong>.</li> +</ol> +</li> +</ol> + +<p>Now you're ready to build your layout with <code>ConstraintLayout</code>.</p> + +<h3 id="convert">Convert a layout</h3> + +<div class="figure" style="width:415px"> +<img src="/training/constraint-layout/images/layout-editor-convert-to-constraint_2x.png" + width="415" alt="" /> +<p class="img-caption"> + <b>Figure 4.</b> The menu to convert a layout to <code>ConstraintLayout</code></p> +</div> + +<p>To convert an existing layout to a constraint layout, follow these steps:</p> +<ol> +<li>Open your layout in Android Studio and click the <strong>Design</strong> tab +at the bottom of the editor window. +<li>In the <strong>Component Tree</strong> window, right-click the layout and +click <strong>Convert <em>layout</em> to ConstraintLayout</strong>.</li> +</ol> + +<h3 id="createNew">Create a new layout</h3> + +<p>To start a new constraint layout file, follow these steps:</p> +<ol> +<li>Click anywhere in the <strong>Project</strong> window and then select +<strong>File > New > XML > Layout XML</strong>. +<li>Enter a name for the layout file and enter +"android.support.constraint.ConstraintLayout" for the <b>Root Tag</b>. +<li>Click <strong>Finish</strong>.</li> +</ol> + + +<h2 id="add-a-constraint">Add a constraint</h2> + +<p>Start by dragging a view from the <b>Palette</b> window into the editor. +When you add a view in a <code>ConstraintLayout</code>, it displays a bounding box with square +resizing handles on each corner and circular constraint handles on each side. +</p> + + +<div class="figure" style="width:460px"> +<div class="video-wrapper"> +<video controls poster="/training/constraint-layout/images/thumbnail-studio-constraint-first.png" + onclick="this.play()" width="460"> + <source src="https://storage.googleapis.com/androiddevelopers/videos/studio/studio-constraint-first.mp4" type="video/mp4"> + <img src="/training/constraint-layout/images/thumbnail-studio-constraint-first" alt="" /> +</video> +</div> +<p class="img-caption"> +<strong>Video 1. </strong>The left side of a view is constrained to the left side of the parent +</p> +</div> + +<p> +Click the view to select it. Then click-and-hold one of the +constraint handles and drag the line to an available anchor point (the edge of +another view, the edge of the layout, or a guideline). When you release, the +constraint is made, with <a href="#adjust-the-view-margins">a default margin</a> +separating the two views. +</p> + +<p>When creating constraints, remember the following rules:</p> + +<ul> +<li>Every view must have at least two constraints: one horizontal and one +vertical. +<li>You can create constraints only between a constraint handle and an anchor +point that share the same plane. So a vertical plane (the left and right sides) +of a view can be constrained only to another vertical plane; and baselines can +constrain only to other baselines. +<li>Each constraint handle can be used for just one constraint, but you can +create multiple constraints (from different views) to the same anchor +point.</li> +</ul> + + + +<div class="figure" style="width:460px"> +<div class="video-wrapper"> +<video controls poster="/training/constraint-layout/images/thumbnail-studio-constraint-second.png" + onclick="this.play()" width="460"> + <source src="https://storage.googleapis.com/androiddevelopers/videos/studio/studio-constraint-second.mp4" type="video/mp4"> + <img src="/training/constraint-layout/images/thumbnail-studio-constraint-second.png" alt="" /> +</video> +</div> +<p class="img-caption"> +<strong>Video 2. </strong>Adding a constraint that opposes an existing one +</p> +</div> + + + +<p> +To remove a constraint, select the view and then click the constraint handle. +</p> + +<p>If you add opposing constraints on a view, the constraint lines become squiggly +like a spring to indicate the opposing forces, as shown in video 2. The effect +is most visible when the view size is set to "fixed" or "wrap content," in which +case the view is centered between the constraints. If you instead +want the view to stretch its size to meet the constraints, <a +href="#adjust-the-view-size">switch the size to "any size"</a>; or if you want +to keep the current size but move the view so that it is not centered, <a +href="#adjust-the-constraint-bias">adjust the constraint bias</a>. +</p> + + + +<p> +There are many ways to constrain a view, but the following constraint types +provide the basic building blocks. +</p> + + + + +<h3>Parent constraint</h3> +<div class="cols"> +<div class="col-2of3"> + <p> + Connect the side of a view to the corresponding edge of the layout. + <p> + In figure 5, the left side of a view is connected to the left edge of the + parent layout. + <p> +</div> +<div class="col-1of3"> + <img src="/training/constraint-layout/images/parent-constraint_2x.png" width="100%" alt=""> + <p class="img-caption"><strong>Figure 5. </strong>A horizontal constraint to the parent</p> +</div> +</div> + + +<h3>Position constraint</h3> +<div class="cols"> +<div class="col-2of3"> +<p>Define the order of appearance for two views, either vertically or horizontally.</p> +<p>In figure 6, a <code>Button</code> is constrained below an <code>ImageView</code> with a 24dp +margin.</p> +</div> +<div class="col-1of3"> + <img src="/training/constraint-layout/images/position-constraint_2x.png" width="100%" alt=""> + <p class="img-caption"><strong>Figure 6.</strong> A vertical position constraint</p> +</div> +</div> + + + +<h3>Alignment constraint</h3> +<div class="cols"> +<div class="col-1of3"> +<p>Align the edge of a view to the same edge of another view.<p> +<p>In figure 7, the left side of a <code>Button</code> is aligned to the left side of an +<code>ImageView</code>.</p> +<p>You can offset the alignment by dragging the view +inward from the constraint. For example, figure 8 shows the same +<code>Button</code> with a 24dp offset alignment. +The offset is defined by the constrained view's margin.</p> +</div> +<div class="col-1of3"> + <img src="/training/constraint-layout/images/alignment-constraint_2x.png" width="100%" alt=""> + <p class="img-caption"><strong>Figure 7.</strong> A horizontal alignment constraint</p> +</div> +<div class="col-1of3"> + <img src="/training/constraint-layout/images/alignment-constraint-offset_2x.png" width="100%" + alt=""> + <p class="img-caption"><strong>Figure 8.</strong> An offset horizontal alignment constraint</p> +</div> +</div> + + +<h3>Baseline alignment constraint</h3> +<div class="cols"> +<div class="col-2of3"> +<p>Align the text baseline of a view to the text baseline of another view.</p> +<p>In figure 9, the first line of a <code>TextView</code> is aligned with the text in a +<code>Button</code>.</p> +<p>To create a baseline constraint, hover your mouse over the baseline handle for +two seconds until the handle blinks white. Then click and drag the line to +another baseline.</p> +</div> +<div class="col-1of3"> + <img src="/training/constraint-layout/images/baseline-constraint_2x.png" width="100%" alt=""> + <p class="img-caption"><strong>Figure 9.</strong> A baseline alignment constraint</p> +</div> +</div> + + + + + +<h3 id="constrain-to-a-guideline">Constrain to a guideline</h3> +<div class="cols"> +<div class="col-1of2"> + +<p> +You can add a vertical or horizontal guideline to which you can attach +constraints. You can position the guideline within the layout based on either dp +units or percent, relative to the layout's edge. +</p> + +<p> +To create a guideline, click <strong>Guidelines</strong> +<img src="/studio/images/buttons/layout-editor-guidelines.png" class="inline-icon" alt="" /> +in the toolbar, and then click either <strong>Add Vertical Guideline</strong> +or <strong>Add Horizontal Guideline</strong>. +</p> + +<p> +Click the circle at the edge of the guideline to toggle the measurements used to +position the guideline (either percent or dp units from the layout's edge). +</p> + +<p> +Guidelines are not visible to your users. +</p> +</div> + +<div class="col-1of2"> + <div class="video-wrapper"> + <video controls poster="/training/constraint-layout/images/thumbnail-add-layout-guideline_2-2.png" + onclick="this.play()" width="100%"> + <source src="https://storage.googleapis.com/androiddevelopers/videos/studio/add-layout-guideline_2-2.mp4" type="video/mp4"> + <img src="/training/constraint-layout/images/thumbnail-add-layout-guideline_2-2.png" alt="" /> + </video> + </div> + <p class="img-caption"><strong>Video 3.</strong> Adding a constraint to a guideline</p> +</div> +</div> + + +<h2 id="use-autoconnect-and-infer-constraints">Use Autoconnect and Infer Constraints</h2> + +<div class="figure" style="width:460px"> +<div class="video-wrapper"> +<video controls poster="" + onclick="this.play()" width="460"> + <source src="https://storage.googleapis.com/androiddevelopers/videos/studio/constraint-autoconnect_2-2.mp4" type="video/mp4"> +</video> +</div> +<p class="img-caption"><b>Video 4.</b> Adding a view with Autoconnect enabled</p> +</div> + +<p> +Autoconnect is a persistent mode that automatically creates two or more +constraints for each view you add to the layout. Autoconnect is disabled by +default. You can enable it by clicking <strong>Turn on Autoconnect</strong> +<img src="/studio/images/buttons/layout-editor-autoconnect-on.png" class="inline-icon" alt="" /> +in the Layout Editor toolbar. +</p> + +<p>While enabled, Autoconnect creates constraints for each view as you add them; it does not create +constraints for existing views in the layout. If you drag a view once the constraints are made, the +constraints do not change (though the margins do), so you must delete the constraints if you want to +significantly reposition the view.</p> + +<p>Alternatively, you can click <strong>Infer Constraints</strong> +<img src="/studio/images/buttons/layout-editor-infer.png" class="inline-icon" alt="" /> +to create constraints for all views in the layout. +</p> + +<p>Infer Constraints is a one-time action that scans the entire layout to determine the most +effective set of constraints for all views, so it may create constraints between elements that are +far from each other. Autoconnect, however, creates constraints only for the view you are adding, and +it creates constraints to only the nearest elements. In either case, you can always modify a +constraint by clicking the constraint handle to delete it, and then create a new constraint.</p> + + +<h2 id="adjust-the-view-size">Adjust the view size</h2> + +<p> +You can use the handles on each corner of the view to resize it, but doing so +hard codes the width and height values, which you should avoid for most views +because hard-coded view sizes cannot adapt to different content and screen +sizes. To select from one of the dynamic sizing modes or to define more specific +dimensions, click a view and open the <strong>Properties</strong> +<img src="/studio/images/buttons/window-properties.png" class="inline-icon" alt="" /> +window on the right side of the editor. At the top of the window is the view +inspector, as shown in figure 10. +</p> +<div class="figure" style="width:287px" > +<img src="/training/constraint-layout/images/layout-editor-properties-callouts_2-2_2x.png" alt="" + width="287" /> +<p class="img-caption"><strong>Figure 10.</strong> The <b>Properties</b> window includes controls for +<strong>(1)</strong> view size, <strong>(2)</strong> margins, and +<strong>(3)</strong> constraint bias.</p> +</div> + +<p> +The grey square represents the selected view. The symbols inside the square +represent the height and width settings as follows: +</p> + +<ul> +<li> +<img src="/studio/images/buttons/layout-width-wrap.png" class="inline-icon" alt="" /> + <strong>Wrap Content</strong>: The view expands exactly as needed to fit its +contents. +<li> +<img src="/studio/images/buttons/layout-width-match.png" class="inline-icon" alt="" /> + <strong>Any Size</strong>: The view expands exactly as needed to match the +constraints. The actual value is 0dp because the view has no desired dimensions, but +it resizes as needed to meet the constraints. However, if the given dimension +has only one constraint, then the view expands to fit its contents. Another way +to think of it is "match constraints" (instead of <code>match_parent</code>) because it +expands the view as much as possible after accounting for the limits of each +constraint and its margins. +<li> +<img src="/studio/images/buttons/layout-width-fixed.png" class="inline-icon" alt="" /> + <strong>Fixed</strong>: You specify the dimension in the text box below or by +resizing the view in the editor.</li> +</ul> + +<p>To toggle between these settings, click the symbols.</p> + +<p class="note"><strong>Note</strong>: You should not use <code>match_parent</code> for any view +in a <code>ConstraintLayout</code>. Instead use "Any Size" (<code>0dp</code>). +</p> + + +<h2 id="adjust-the-constraint-bias">Adjust the constraint bias</h2> + +<p>When you add a constraint to both sides of a view (and the view size for the same dimension is +either "fixed" or "wrap content"), the view becomes centered between the two anchor points by +default. When a view is centered, the bias is 50%. You can adjust the bias by dragging the bias +slider in the <b>Properties</b> window or by dragging the view, as shown in video 5.</p> + +<div class="video-wrapper" style="max-width:740px"> +<video controls poster="/training/constraint-layout/images/thumbnail-adjust-constraint-bias.png" + onclick="this.play();$(this.parentElement).addClass('playing');"> + <source src="https://storage.googleapis.com/androiddevelopers/videos/studio/adjust-constraint-bias.mp4" type="video/mp4"> + <img src="/training/constraint-layout/images/thumbnail-adjust-constraint-bias.png" alt="" /> +</video> +</div> +<p class="img-caption"><b>Video 5.</b> Adjusting the constraint bias</p> + +<p>If you instead want the view to stretch its size to meet the constraints, <a href="#adjust-the- +view-size">switch the size to "any size"</a>.</p> + + +<h2 id="adjust-the-view-margins">Adjust the view margins</h2> + +<p> To ensure that all your views are evenly spaced, click <strong>Margin</strong> <img +src="/studio/images/buttons/layout-editor-margin.png" class="inline-icon" alt="" /> in the toolbar +to select the default margin for each view that you add to the layout. The button changes to show +your current margin selection. Any change you make to the default margin applies only to the views +you add from then on. </p> + + +<img src="/training/constraint-layout/images/layout-editor-margin-callout_2-2_2x.png" + alt="" width="232"/> +<p class="img-caption"><strong>Figure 11.</strong> The toolbar's <b>Margin</b> button. +Click to adjust the default margin. +</p> + +<p> You can control the margin for each view in the <strong>Properties</strong> window by clicking +the number on the line that represents each constraint (in figure 10, the margins are each set to +16dp). </p> + +<p> All margins offered by the tool are factors of 8dp to help your views align to Material Design's +<a href="https://material.google.com/layout/metrics-keylines.html">8dp square grid +recommendations</a>. </p> + +</body> +</html> diff --git a/docs/html/training/material/images/palette-library-color-profiles_2-1_2x.png b/docs/html/training/material/images/palette-library-color-profiles_2-1_2x.png Binary files differnew file mode 100644 index 000000000000..d14ec32c7500 --- /dev/null +++ b/docs/html/training/material/images/palette-library-color-profiles_2-1_2x.png diff --git a/docs/html/training/material/images/palette-library-title-text-color_2-1_2x.png b/docs/html/training/material/images/palette-library-title-text-color_2-1_2x.png Binary files differnew file mode 100644 index 000000000000..883adba6a430 --- /dev/null +++ b/docs/html/training/material/images/palette-library-title-text-color_2-1_2x.png diff --git a/docs/html/training/material/index.jd b/docs/html/training/material/index.jd index 4001e6b7442f..8baa0659e95b 100644 --- a/docs/html/training/material/index.jd +++ b/docs/html/training/material/index.jd @@ -3,7 +3,6 @@ page.type=design page.image=images/cards/material_2x.png page.metaDescription=Learn how to apply material design to your apps. - @jd:body <div id="tb-wrapper"> @@ -58,6 +57,9 @@ specification</a> and use the new components and functionality available in Andr <dt><a href="{@docRoot}training/material/compatibility.html">Maintaining Compatibility</a></dt> <dd>Learn how to maintain compatibility with platform versions earlier than Android 5.0.</dd> + + <dt><a href="{@docRoot}training/material/palette-colors.html">Selecting Colors with the Palette API</a></dt> + <dd>Learn how to select colors for your app using the v7 Palette library.</dd> </dl> <h2>Video Training</h2> diff --git a/docs/html/training/material/palette-colors.html b/docs/html/training/material/palette-colors.html new file mode 100644 index 000000000000..27485d2decd8 --- /dev/null +++ b/docs/html/training/material/palette-colors.html @@ -0,0 +1,310 @@ +<html devsite> +<head> + <title>Selecting Colors with the Palette API</title> + <meta name="book_path" value="/training/_book.yaml" /> + <meta name="top_category" value="develop" /> + <meta name="subcategory" value="training" /> +</head> +<body> + +<div id="tb-wrapper"> + <div id="tb"> + <h2>This lesson teaches you to</h2> + <ol> + <li><a href="#set-up-the-library">Set up the library</a></li> + <li><a href="#create-a-palette">Create a palette</a> + <ol> + <li><a href="#generate-a-palette-instance">Generate a Palette instance</a></li> + <li><a href="#customize-your-palette">Customize your palette</a></li> + </ol> + </li> + <li><a href="#extract-color-profiles">Extract color profiles</a> + <ol> + <li><a href="#use-swatches">Use swatches to create color schemes</a></li> + </ol> + </li> + </ol> + <h2>You should also read</h2> + <ul> + <li><a href="http://www.google.com/design/spec">Material design specification</a></li> + <li><a href="/design/material/index.html">Material design on Android</a></li> + </ul> + </div> +</div> + +<p>Good visual design is essential for a successful app, and color schemes are a primary component of design. The palette library is a +<a href="/topic/libraries/support-library/features.html#v7-palette">support library</a> +that extracts prominent colors from images to help you create visually engaging apps.</p> + +<p>You can use the palette library to design layout +<a href="/guide/topics/ui/themes.html">themes</a> and apply custom colors to visual elements in your app. +For example, you can use a palette to create a color-coordinated title +card for a song based on its album cover or to adjust an app’s toolbar color when its +background image changes. The <code><a +href="/reference/android/support/v7/graphics/Palette.html">Palette</a></code> object gives +you access to the colors in a <code><a +href="/reference/android/graphics/Bitmap.html">Bitmap</a></code> +image while also providing six main color profiles from the bitmap to help +inform your <a href="http://material.google.com">design choices</a>.</p> + +<h2 id="set-up-the-library">Set up the library</h2> + +<p>To use the palette library, install or update the <a +href="/topic/libraries/support-library/index.html">Android +Support Library</a> to version 24.0.0 or higher and follow the instructions for <a +href="/topic/libraries/support-library/setup.html#add-library">Adding +Support Libraries</a> to add the palette library to your app development project.</p> + +<p>Make sure that the version specified in your dependency identifier matches your +app’s <code>compileSdkVersion</code>, set in the <code>build.gradle</code> +file:</p> + +<pre class="prettyprint"> +android { + compileSdkVersion 24 + ... +} + +dependencies { + ... + compile 'com.android.support:palette-v7:24.2.1' +} +</pre> + +<p>For more information about adding the palette dependency, read about the palette +feature in the <a +href="/topic/libraries/support-library/features.html#v7-palette">support +library documentation</a>.</p> + +<h2 id="create-a-palette">Create a palette</h2> + +<p>A <code>Palette</code> object gives you access to the primary colors in an +image, as well as the corresponding colors for overlaid text. Use palettes to design +your app’s style and to dynamically change your app’s color scheme based on a +given source image.</p> + +<p>To create a palette, first instantiate a <code><a +href="https://developer.android.com/reference/android/support/v7/graphics/Palette.Builder.html">Palette.Builder</a></code> +from a <code>Bitmap</code>. You can then use the +<code>Palette.Builder</code> to customize the palette before generating it. This +section will describe palette generation and customization from a bitmap +image.</p> + +<h3 id="generate-a-palette-instance">Generate a Palette instance</h3> + +<p>Generate a <code>Palette</code> instance using <code>Palette</code>’s +<code><a +href="/reference/android/support/v7/graphics/Palette.html#from(android.graphics.Bitmap)">from(Bitmap +bitmap)</a></code> method to first create a <code>Palette.Builder</code> +from a <code>Bitmap</code>. The builder can then generate the palette either +synchronously or asynchronously.</p> + +<p>Use synchronous palette generation if you want to create the palette on +the same thread as the method being called. If you generate the palette +asynchronously on a different thread, use the <code><a +href="/reference/android/support/v7/graphics/Palette.PaletteAsyncListener.html#onGenerated(android.support.v7.graphics.Palette)">onGenerated()</a></code> +method to access the palette immediately after it has been created.</p> + +<p>The following code snippet provides example methods for both types of palette generation:</p> + +<pre class="prettyprint"> +// Generate palette synchronously and return it +public Palette createPaletteSync(Bitmap bitmap) { + Palette p = Palette.from(bitmap).generate(); + return p; +} + +// Generate palette asynchronously and use it on a different +// thread using onGenerated() +public void createPaletteAsync(Bitmap bitmap) { + Palette.from(bitmap).generate(new PaletteAsyncListener() { + public void onGenerated(Palette p) { + // Use generated instance + } + }); +} +</pre> + +<p>If you need to continuously generate palettes for a sorted list of images +or objects, consider <a +href="/reference/android/util/LruCache.html">caching</a> +the <code>Palette</code> instances to prevent slow UI performance. You also +should not create the palettes on your <a href="/training/articles/perf-anr.html">main thread</a>.</p> + +<h3 id="customize-your-palette">Customize your palette</h3> + +<p>The <code>Palette.Builder</code> allows you to customize your palette by +choosing how many colors are in the resulting palette, what area of your +image the builder uses to generate the palette, and what colors are allowed in the +palette. For example, you can filter out the color black or ensure that the +builder only uses the top half of an image to generate your palette.</p> + +<p>Fine-tune your palette’s size and colors with the following methods from +the <code>Palette.Builder</code> class:</p> + +<dl> + + <dt><code><a + href="/reference/android/support/v7/graphics/Palette.Builder.html#addFilter(android.support.v7.graphics.Palette.Filter)">addFilter()</a></code></dt> + <dd>This method adds a filter that indicates what colors are allowed in the + resulting palette. Pass in your own<code> <a + href="/reference/android/support/v7/graphics/Palette.Filter.html">Palette.Filter</a></code> + and modify its <code>isAllowed()</code> method to determine which colors are + filtered from the palette.</dd> + + <dt><code><a + href="/reference/android/support/v7/graphics/Palette.Builder.html#maximumColorCount(int)">maximumColorCount()</a></code></dt> + <dd>This method sets the maximum number of colors in your palette. The + default value is 16, and the optimal value depends on the source image. + For landscapes, optimal values range from 8-16 while pictures with faces + usually have values that fall between 24-32. The + <code>Palette.Builder</code> takes longer to generate palettes with more + colors.</dd> + + <dt><code><a + href="/reference/android/support/v7/graphics/Palette.Builder.html#setRegion(int,%20int,%20int,%20int)">setRegion()</a></code></dt> + <dd>This method indicates what area of the bitmap the builder uses when + creating the palette. You can only use this method when generating the palette from + a bitmap, and it does not affect the original image.</dd> + + <dt><code><a + href="/reference/android/support/v7/graphics/Palette.Builder.html#addTarget(android.support.v7.graphics.Target)">addTarget()</a></code></dt> + <dd>This method allows you to perform your own color matching by adding a + <code><a + href="/reference/android/support/v7/graphics/Target.html">Target</a></code> + color profile to the builder. If the default <code>Target</code>s are not + sufficient, advanced developers can create their own <code>Target</code>s + using a <code><a + href="/reference/android/support/v7/graphics/Target.Builder.html">Target.Builder</a></code>.</dd> + +</dl> + +<h2 id="extract-color-profiles">Extract color profiles</h2> + +<p>Based on the <a +href="https://material.google.com/style/color.html#">standards +of material design</a>, the palette library extracts commonly used color +profiles from an image. Each profile is defined by a <code><a +href="/reference/android/support/v7/graphics/Target.html">Target</a></code>, +and colors extracted from the bitmap image are scored against each profile +based on saturation, luminance, and population (number of pixels in the bitmap +represented by the color). For each profile, the color with the best score +defines that color profile for the given image.</p> + +<p>By default, a <code>Palette</code> object contains 16 primary colors from +a given image. When generating your palette, you can <a +href="#customize-your-palette">customize</a> its number of colors using the +<code>Palette.Builder</code>. Extracting more colors provides more potential +matches for each color profile but also causes <code>Palette.Builder</code> to +take longer when generating the palette.</p> + +<p>The palette library attempts to extract the following six color +profiles:</p> + +<ul> + <li>Light Vibrant</li> + <li>Vibrant</li> + <li>Dark Vibrant</li> + <li>Light Muted</li> + <li>Muted</li> + <li>Dark Muted</li> +</ul> + +<p>Each of <code>Palette</code>’s <code>get<<em>Profile</em>>Color()</code> +methods returns the color in the palette associated with that particular profile, +where <code><<em>Profile</em>></code> is replaced by the name of one of the six +color profiles. For example, the method to get the Dark Vibrant color profile is <code><a +href="/reference/android/support/v7/graphics/Palette.html#getDarkVibrantColor(int)">getDarkVibrantColor()</a></code>. +Since not all images will contain all color profiles, you must also provide +a default color to return.</p> + +<p>Figure 1 displays a photo and its corresponding color +profiles from the <code>get<<em>Profile</em>>Color()</code> methods.</p> + +<img src="/training/material/images/palette-library-color-profiles_2-1_2x.png" alt="" width="624"/> + +<p class="img-caption"><strong>Figure 1.</strong> An example image and its +extracted color profiles given the default maximum color count (16) for the palette.</p> + +<h3 id="use-swatches">Use swatches to create color schemes</h3> + +<p>The <code>Palette</code> class also generates <code><a +href="/reference/android/support/v7/graphics/Palette.Swatch.html">Palette.Swatch</a></code> +objects for each color profile. <code>Palette.Swatch</code> +objects contain the associated color for that profile, as well as the +color’s population in pixels.</p> + +<p>Swatches have additional methods for accessing more information about the color +profile, such as HSL values and pixel population. You can use swatches to help +create more comprehensive color schemes and app themes using the <code><a +href="/reference/android/support/v7/graphics/Palette.Swatch.html#getBodyTextColor()">getBodyTextColor()</a></code> +and <code><a +href="/reference/android/support/v7/graphics/Palette.Swatch.html#getTitleTextColor()">getTitleTextColor()</a></code> +methods. These methods return colors appropriate for use over the swatch’s +color.</p> + +<p>Each of <code>Palette</code>’s <code>get<<em>Profile</em>>Swatch()</code> +methods returns the swatch associated with that particular profile, +where <code><<em>Profile</em>></code> is replaced by the name of one of the six +color profiles. Although the palette’s <code>get<<em>Profile</em>>Swatch()</code> methods +do not require default value parameters, they return <code>null</code> if that +particular profile does not exist in the image. Therefore, you should check that +a swatch is not null before using it. For example, the following method +returns the Vibrant swatch from a palette if the swatch is not null:</p> + +<pre class="prettyprint"> +// Return a palette's vibrant swatch after checking that it exists +private Palette.Swatch checkVibrantSwatch(Palette p) { + Palette.Swatch vibrant = p.getVibrantSwatch(); + if (vibrant != null) { + return vibrant; + } + // Throw error +} +</pre> + +<p>To access all colors in a palette, the <code><a +href="/reference/android/support/v7/graphics/Palette.html#getSwatches()">getSwatches()</a></code> +method returns a list of all swatches generated from an +image, including the standard six color profiles.</p> + +<p>The following snippet of code uses the methods from the above code snippets to +synchronously generate a palette, get its vibrant swatch, and change the colors of a +toolbar to match the bitmap image. Figure 2 displays the resulting image and toolbar.</p> + +<div class="cols"> + + <div class="col-2of3"> + +<pre class="prettyprint"> +// Set the background and text colors of a toolbar given a +// bitmap image to match +public void setToolbarColor(Bitmap bitmap) { + // Generate the palette and get the vibrant swatch + // See the createPaletteSync() and checkVibrantSwatch() methods + // from the code snippets above + Palette p = createPaletteSync(bitmap); + Palette.Swatch vibrantSwatch = checkVibrantSwatch(p); + + // Set the toolbar background and text colors + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + toolbar.setBackgroundColor(vibrantSwatch.getRgb()); + toolbar.setTitleTextColor(vibrantSwatch.getTitleTextColor()); +} +</pre> + + </div> + + <div class="col-1of3"> + + <img src="/training/material/images/palette-library-title-text-color_2-1_2x.png" alt="" width="400"/> + + <p class="img-caption"><strong>Figure 2.</strong> An example image with its + vibrant-colored toolbar and corresponding title text color.</p> + + </div> + +</div> + +</body> +</html> diff --git a/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd b/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd index df8b1bc5c9a6..dc94bdf05502 100644 --- a/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd +++ b/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd @@ -53,8 +53,9 @@ you choose, to simulate any dependency relationships.</p> <p>In your Android Studio project, you must store the source files for instrumented tests at -<code><var>module-name</var>/src/androidTests/java/</code>. This directory -already exists when you create a new project.</p> +<code><var>module-name</var>/src/androidTest/java/</code>. This directory +already exists when you create a new project and contains an example +instrumented test.</p> <p>Before you begin, you should <a href="{@docRoot}tools/testing-support-library/index.html#setup">download diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index 0f305f3cff3d..dcca431ea754 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -65,19 +65,36 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; /** - * This class uses {@link android.animation.ObjectAnimator} and - * {@link android.animation.AnimatorSet} to animate the properties of a - * {@link android.graphics.drawable.VectorDrawable} to create an animated drawable. + * This class animates properties of a {@link android.graphics.drawable.VectorDrawable} with + * animations defined using {@link android.animation.ObjectAnimator} or + * {@link android.animation.AnimatorSet}. * <p> - * AnimatedVectorDrawable are normally defined as 3 separate XML files. + * Starting from API 25, AnimatedVectorDrawable runs on RenderThread (as opposed to on UI thread for + * earlier APIs). This means animations in AnimatedVectorDrawable can remain smooth even when there + * is heavy workload on the UI thread. Note: If the UI thread is unresponsive, RenderThread may + * continue animating until the UI thread is capable of pushing another frame. Therefore, it is not + * possible to precisely coordinate a RenderThread-enabled AnimatedVectorDrawable with UI thread + * animations. Additionally, + * {@link android.graphics.drawable.Animatable2.AnimationCallback#onAnimationEnd(Drawable)} will be + * called the frame after the AnimatedVectorDrawable finishes on the RenderThread. * </p> * <p> - * First is the XML file for {@link android.graphics.drawable.VectorDrawable}. - * Note that we allow the animation to happen on the group's attributes and path's - * attributes, which requires they are uniquely named in this XML file. Groups - * and paths without animations do not need names. + * AnimatedVectorDrawable can be defined in either <a href="#ThreeXML">three separate XML files</a>, + * or <a href="#OneXML">one XML</a>. * </p> - * <li>Here is a simple VectorDrawable in this vectordrawable.xml file. + * <a name="ThreeXML"></a> + * <h3>Define an AnimatedVectorDrawable in three separate XML files</h3> + * <ul> + * <a name="VDExample"></a> + * <li><h4>XML for the VectorDrawable containing properties to be animated</h4> + * <p> + * Animations can be performed on both group and path attributes, which requires groups and paths to + * have unique names in the same VectorDrawable. Groups and paths without animations do not need to + * be named. + * </p> + * Below is an example of a VectorDrawable defined in vectordrawable.xml. This VectorDrawable is + * referred to by its file name (not including file suffix) in the + * <a href="AVDExample">AnimatedVectorDrawable XML example</a>. * <pre> * <vector xmlns:android="http://schemas.android.com/apk/res/android" * android:height="64dp" @@ -96,17 +113,20 @@ import java.util.ArrayList; * </group> * </vector> * </pre></li> + * + * <a name="AVDExample"></a> + * <li><h4>XML for AnimatedVectorDrawable</h4> * <p> - * Second is the AnimatedVectorDrawable's XML file, which defines the target - * VectorDrawable, the target paths and groups to animate, the properties of the - * path and group to animate and the animations defined as the ObjectAnimators - * or AnimatorSets. + * An AnimatedVectorDrawable element has a VectorDrawable attribute, and one or more target + * element(s). The target elements can be the path or group to be animated. Each target element + * contains a name attribute that references a property (of a path or a group) to animate, and an + * animation attribute that points to an ObjectAnimator or an AnimatorSet. * </p> - * <li>Here is a simple AnimatedVectorDrawable defined in this avd.xml file. - * Note how we use the names to refer to the groups and paths in the vectordrawable.xml. + * The following code sample defines an AnimatedVectorDrawable. Note that the names refer to the + * groups and paths in the <a href="#VDExample">VectorDrawable XML above</a>. * <pre> * <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" - * android:drawable="@drawable/vectordrawable" > + * android:drawable="@drawable/vectordrawable" > * <target * android:name="rotationGroup" * android:animation="@anim/rotation" /> @@ -114,39 +134,43 @@ import java.util.ArrayList; * android:name="v" * android:animation="@anim/path_morph" /> * </animated-vector> - * </pre></li> + * </pre> + * </li> + * + * <li><h4>XML for Animations defined using ObjectAnimator or AnimatorSet</h4> * <p> - * Last is the Animator XML file, which is the same as a normal ObjectAnimator - * or AnimatorSet. - * To complete this example, here are the 2 animator files used in avd.xml: - * rotation.xml and path_morph.xml. + * From the previous <a href="#AVDExample">example of AnimatedVectorDrawable</a>, two animations + * were used: rotation.xml and path_morph.xml. * </p> - * <li>Here is the rotation.xml, which will rotate the target group for 360 degrees. + * rotation.xml rotates the target group from 0 degree to 360 degrees over 6000ms: * <pre> * <objectAnimator * android:duration="6000" * android:propertyName="rotation" * android:valueFrom="0" * android:valueTo="360" /> - * </pre></li> - * <li>Here is the path_morph.xml, which will morph the path from one shape to - * the other. Note that the paths must be compatible for morphing. - * In more details, the paths should have exact same length of commands , and - * exact same length of parameters for each commands. - * Note that the path strings are better stored in strings.xml for reusing. + * </pre> + * + * path_morph.xml morphs the path from one shape into the other. Note that the paths must be + * compatible for morphing. Specifically, the paths must have the same commands, in the same order, + * and must have the same number of parameters for each command. It is recommended to store path + * strings as string resources for reuse. * <pre> * <set xmlns:android="http://schemas.android.com/apk/res/android"> * <objectAnimator * android:duration="3000" * android:propertyName="pathData" - * android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z" + * android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z" * android:valueTo="M300,70 l 0,-70 70,0 0,140 -70,0 z" * android:valueType="pathType"/> * </set> - * </pre></li> + * </pre> + * </ul> + * <a name="OneXML"></a> + * <h3>Define an AnimatedVectorDrawable all in one XML file</h3> * <p> - * Since AAPT tool is now supporting a new format which can bundle several related XML files into - * one, we can merge the previous example into one XML file, like this: + * Since the AAPT tool supports a new format that bundles several related XML files together, we can + * merge the XML files from the previous examples into one XML file: * </p> * <pre> * <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" > @@ -185,7 +209,7 @@ import java.util.ArrayList; * <objectAnimator * android:duration="3000" * android:propertyName="pathData" - * android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z" + * android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z" * android:valueTo="M300,70 l 0,-70 70,0 0,140 -70,0 z" * android:valueType="pathType"/> * </set> @@ -286,6 +310,17 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { return super.getChangingConfigurations() | mAnimatedVectorState.getChangingConfigurations(); } + /** + * Draws the AnimatedVectorDrawable into the given canvas. + * <p> + * <strong>Note:</strong> Calling this method with a software canvas when the + * AnimatedVectorDrawable is being animated on RenderThread (for API 25 and later) may yield + * outdated result, as the UI thread is not guaranteed to be in sync with RenderThread on + * VectorDrawable's property changes during RenderThread animations. + * </p> + * + * @param canvas The canvas to draw into + */ @Override public void draw(Canvas canvas) { if (!canvas.isHardwareAccelerated() && mAnimatorSet instanceof VectorDrawableAnimatorRT) { @@ -321,9 +356,9 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { } /** - * AnimatedVectorDrawable is running on render thread now. Therefore, if the root alpha is being - * animated, then the root alpha value we get from this call could be out of sync with alpha - * value used in the render thread. Otherwise, the root alpha should be always the same value. + * For API 25 and later, AnimatedVectorDrawable runs on RenderThread. Therefore, when the + * root alpha is being animated, this getter does not guarantee to return an up-to-date alpha + * value. * * @return the containing vector drawable's root alpha value. */ @@ -1495,7 +1530,6 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { } else { addPendingAction(START_ANIMATION); } - } @Override diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java index 2cbeb3a260be..ed718493bd78 100644 --- a/media/java/android/media/ImageReader.java +++ b/media/java/android/media/ImageReader.java @@ -241,6 +241,10 @@ public class ImageReader implements AutoCloseable { * same {@link Surface} can be reused with a different API once the first source is * disconnected from the {@link Surface}.</p> * + * <p>Please note that holding on to the Surface object returned by this method is not enough + * to keep its parent ImageReader from being reclaimed. In that sense, a Surface acts like a + * {@link java.lang.ref.WeakReference weak reference} to the ImageReader that provides it.</p> + * * @return A {@link Surface} to use for a drawing target for various APIs. */ public Surface getSurface() { diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java index 458672a41b77..e6e0243c53e1 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java @@ -32,6 +32,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.support.v4.widget.DrawerLayout; +import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import android.util.Pair; @@ -58,6 +59,7 @@ public class SettingsDrawerActivity extends Activity { protected static final boolean DEBUG_TIMING = false; private static final String TAG = "SettingsDrawerActivity"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); public static final String EXTRA_SHOW_MENU = "show_drawer_menu"; @@ -111,7 +113,7 @@ public class SettingsDrawerActivity extends Activity { public void onItemClick(android.widget.AdapterView<?> parent, View view, int position, long id) { onTileClicked(mDrawerAdapter.getTile(position)); - }; + } }); mUserManager = UserManager.get(this); @@ -143,8 +145,16 @@ public class SettingsDrawerActivity extends Activity { new CategoriesUpdater().execute(); } - if (getIntent() != null && getIntent().getBooleanExtra(EXTRA_SHOW_MENU, false)) { - showMenuIcon(); + final Intent intent = getIntent(); + if (intent != null) { + if (intent.hasExtra(EXTRA_SHOW_MENU)) { + if (intent.getBooleanExtra(EXTRA_SHOW_MENU, false)) { + // Intent explicitly set to show menu. + showMenuIcon(); + } + } else if (isTopLevelTile(intent)) { + showMenuIcon(); + } } } @@ -157,6 +167,30 @@ public class SettingsDrawerActivity extends Activity { super.onPause(); } + private boolean isTopLevelTile(Intent intent) { + final ComponentName componentName = intent.getComponent(); + if (componentName == null) { + return false; + } + // Look for a tile that has the same component as incoming intent + final List<DashboardCategory> categories = getDashboardCategories(); + for (DashboardCategory category : categories) { + for (Tile tile : category.tiles) { + if (TextUtils.equals(tile.intent.getComponent().getClassName(), + componentName.getClassName())) { + if (DEBUG) { + Log.d(TAG, "intent is for top level tile: " + tile.title); + } + return true; + } + } + } + if (DEBUG) { + Log.d(TAG, "Intent is not for top level settings " + intent); + } + return false; + } + public void addCategoryListener(CategoryListener listener) { mCategoryListeners.add(listener); } @@ -287,9 +321,9 @@ public class SettingsDrawerActivity extends Activity { private void updateUserHandlesIfNeeded(Tile tile) { List<UserHandle> userHandles = tile.userHandle; - for (int i = userHandles.size()-1; i >= 0; i--) { + for (int i = userHandles.size() - 1; i >= 0; i--) { if (mUserManager.getUserInfo(userHandles.get(i).getIdentifier()) == null) { - if (DEBUG_TIMING) { + if (DEBUG) { Log.d(TAG, "Delete the user: " + userHandles.get(i).getIdentifier()); } userHandles.remove(i); diff --git a/packages/SystemUI/res/drawable/ic_qs_data_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_data_disabled.xml index b4144a3474fd..d11b6f461870 100644 --- a/packages/SystemUI/res/drawable/ic_qs_data_disabled.xml +++ b/packages/SystemUI/res/drawable/ic_qs_data_disabled.xml @@ -14,8 +14,9 @@ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24.0dp" - android:height="24.0dp" + android:autoMirrored="true" + android:width="32.0dp" + android:height="32.0dp" android:viewportWidth="40.0" android:viewportHeight="40.0"> <path diff --git a/packages/SystemUI/res/drawable/stat_sys_data_disabled.xml b/packages/SystemUI/res/drawable/stat_sys_data_disabled.xml index 4e2a0245ab2a..694019e98867 100644 --- a/packages/SystemUI/res/drawable/stat_sys_data_disabled.xml +++ b/packages/SystemUI/res/drawable/stat_sys_data_disabled.xml @@ -14,9 +14,9 @@ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="17.0dp" + android:width="8.5dp" android:height="17.0dp" - android:viewportWidth="40.0" + android:viewportWidth="20.0" android:viewportHeight="40.0"> <path android:fillColor="#FFFFFFFF" diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index a21408d4929e..5c8a6e25f6c2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -160,6 +160,10 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha for (QSTile<?> tile : tiles) { QSTileBaseView tileView = mQsPanel.getTileView(tile); + if (tileView == null) { + Log.e(TAG, "tileView is null " + tile.getTileSpec()); + continue; + } final TextView label = ((QSTileView) tileView).getLabel(); final View tileIcon = tileView.getIcon().getIconView(); if (count < mNumQuickTiles && mAllowFancy) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 7bdb1c499bd9..70642edde3ef 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -17,6 +17,7 @@ package com.android.systemui.recents; import android.app.Activity; +import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.TaskStackBuilder; import android.content.BroadcastReceiver; @@ -87,6 +88,7 @@ import com.android.systemui.statusbar.BaseStatusBar; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.List; /** * The main Recents activity that is started from RecentsComponent. @@ -165,18 +167,39 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD */ final BroadcastReceiver mSystemBroadcastReceiver = new BroadcastReceiver() { @Override - public void onReceive(Context context, Intent intent) { + public void onReceive(Context ctx, Intent intent) { String action = intent.getAction(); if (action.equals(Intent.ACTION_SCREEN_OFF)) { // When the screen turns off, dismiss Recents to Home dismissRecentsToHomeIfVisible(false); } else if (action.equals(Intent.ACTION_TIME_CHANGED)) { - // For the time being, if the time changes, then invalidate the - // last-stack-active-time, this ensures that we will just show the last N tasks - // the next time that Recents loads, but prevents really old tasks from showing - // up if the task time is set forward. - Prefs.putLong(RecentsActivity.this, Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, - 0); + // If the time shifts but the currentTime >= lastStackActiveTime, then that boundary + // is still valid. Otherwise, we need to reset the lastStackactiveTime to the + // currentTime and remove the old tasks in between which would not be previously + // visible, but currently would be in the new currentTime + long oldLastStackActiveTime = Prefs.getLong(RecentsActivity.this, + Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, -1); + if (oldLastStackActiveTime != -1) { + long currentTime = System.currentTimeMillis(); + if (currentTime < oldLastStackActiveTime) { + // We are only removing tasks that are between the new current time + // and the old last stack active time, they were not visible and in the + // TaskStack so we don't need to remove any associated TaskViews but we do + // need to load the task id's from the system + RecentsTaskLoadPlan loadPlan = Recents.getTaskLoader().createLoadPlan(ctx); + loadPlan.preloadRawTasks(false /* includeFrontMostExcludedTask */); + List<ActivityManager.RecentTaskInfo> tasks = loadPlan.getRawTasks(); + for (int i = tasks.size() - 1; i >= 0; i--) { + ActivityManager.RecentTaskInfo task = tasks.get(i); + if (currentTime <= task.lastActiveTime && task.lastActiveTime < + oldLastStackActiveTime) { + Recents.getSystemServices().removeTask(task.persistentId); + } + } + Prefs.putLong(RecentsActivity.this, + Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, currentTime); + } + } } } }; diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index a7f271648e13..ab4c811f6a06 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -216,11 +216,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener * {@link Recents#onBusEvent(RecentsVisibilityChangedEvent)}. */ public void onVisibilityChanged(Context context, boolean visible) { - SystemUIApplication app = (SystemUIApplication) context; - PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class); - if (statusBar != null) { - statusBar.updateRecentsVisibility(visible); - } + Recents.getSystemServices().setRecentsVisibility(visible); } /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index b896f8a4d815..930ed795af9f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -69,6 +69,7 @@ import android.util.MutableBoolean; import android.view.Display; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IDockedStackListener; +import android.view.IWindowManager; import android.view.WindowManager; import android.view.WindowManager.KeyboardShortcutsReceiver; import android.view.WindowManagerGlobal; @@ -120,6 +121,7 @@ public class SystemServicesProxy { IPackageManager mIpm; AssistUtils mAssistUtils; WindowManager mWm; + IWindowManager mIwm; UserManager mUm; Display mDisplay; String mRecentsPackage; @@ -207,6 +209,7 @@ public class SystemServicesProxy { mIpm = AppGlobals.getPackageManager(); mAssistUtils = new AssistUtils(context); mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + mIwm = WindowManagerGlobal.getWindowManagerService(); mUm = UserManager.get(context); mDisplay = mWm.getDefaultDisplay(); mRecentsPackage = context.getPackageName(); @@ -1091,6 +1094,28 @@ public class SystemServicesProxy { } } + /** + * Updates the visibility of recents. + */ + public void setRecentsVisibility(boolean visible) { + try { + mIwm.setRecentsVisibility(visible); + } catch (RemoteException e) { + Log.e(TAG, "Unable to reach window manager", e); + } + } + + /** + * Updates the visibility of the picture-in-picture. + */ + public void setTvPipVisibility(boolean visible) { + try { + mIwm.setTvPipVisibility(visible); + } catch (RemoteException e) { + Log.e(TAG, "Unable to reach window manager", e); + } + } + private final class H extends Handler { private static final int ON_TASK_STACK_CHANGED = 1; private static final int ON_ACTIVITY_PINNED = 2; diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java index 1278b735a7cd..9b48e4d02623 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java @@ -248,6 +248,13 @@ public class RecentsTaskLoadPlan { return mStack; } + /** + * Returns the raw list of recent tasks. + */ + public List<ActivityManager.RecentTaskInfo> getRawTasks() { + return mRawTasks; + } + /** Returns whether there are any tasks in any stacks. */ public boolean hasTasks() { if (mStack != null) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java index fca8d2d1fae1..ef9de53682e4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java @@ -140,10 +140,6 @@ public class RecentsTvImpl extends RecentsImpl{ @Override public void onVisibilityChanged(Context context, boolean visible) { - SystemUIApplication app = (SystemUIApplication) context; - TvStatusBar statusBar = app.getComponent(TvStatusBar.class); - if (statusBar != null) { - statusBar.updateRecentsVisibility(visible); - } + Recents.getSystemServices().setRecentsVisibility(visible); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 9251f32099a7..714c88df182e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -2994,10 +2994,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, Integer.toHexString(diff))); boolean sbModeChanged = false; if (diff != 0) { - // we never set the recents bit via this method, so save the prior state to prevent - // clobbering the bit below - final boolean wasRecentsVisible = (mSystemUiVisibility & View.RECENT_APPS_VISIBLE) > 0; - mSystemUiVisibility = newVal; // update low profile @@ -3048,11 +3044,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE; } - // restore the recents bit - if (wasRecentsVisible) { - mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; - } - // send updated sysui visibility to window manager notifyUiVisibilityChanged(mSystemUiVisibility); } @@ -4816,16 +4807,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, return false; } - public void updateRecentsVisibility(boolean visible) { - // Update the recents visibility flag - if (visible) { - mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; - } else { - mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE; - } - notifyUiVisibilityChanged(mSystemUiVisibility); - } - @Override public void showScreenPinningRequest(int taskId) { if (mKeyguardMonitor.isShowing()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index 2d4900b36890..3c83921a1e14 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.tv; import android.content.ComponentName; import android.graphics.Rect; import android.os.IBinder; -import android.os.RemoteException; import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.view.View; @@ -36,16 +35,6 @@ import com.android.systemui.tv.pip.PipManager; public class TvStatusBar extends BaseStatusBar { - /** - * Tracking calls to View.setSystemUiVisibility(). - */ - int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; - - /** - * Last value sent to window manager. - */ - private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE; - @Override public void setIcon(String slot, StatusBarIcon icon) { } @@ -224,40 +213,6 @@ public class TvStatusBar extends BaseStatusBar { putComponent(TvStatusBar.class, this); } - /** - * Updates the visibility of the picture-in-picture. - */ - public void updatePipVisibility(boolean visible) { - if (visible) { - mSystemUiVisibility |= View.TV_PICTURE_IN_PICTURE_VISIBLE; - } else { - mSystemUiVisibility &= ~View.TV_PICTURE_IN_PICTURE_VISIBLE; - } - notifyUiVisibilityChanged(mSystemUiVisibility); - } - - /** - * Updates the visibility of the Recents - */ - public void updateRecentsVisibility(boolean visible) { - if (visible) { - mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; - } else { - mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE; - } - notifyUiVisibilityChanged(mSystemUiVisibility); - } - - private void notifyUiVisibilityChanged(int vis) { - try { - if (mLastDispatchedSystemUiVisibility != vis) { - mWindowManagerService.statusBarVisibilityChanged(vis); - mLastDispatchedSystemUiVisibility = vis; - } - } catch (RemoteException ex) { - } - } - @Override public void handleSystemNavigationKey(int arg1) { // Not implemented diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java index 3f8650a5e116..085e003f8869 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java @@ -723,10 +723,7 @@ public class PipManager { return mPipRecentsOverlayManager; } - private void updatePipVisibility(boolean visible) { - TvStatusBar statusBar = ((SystemUIApplication) mContext).getComponent(TvStatusBar.class); - if (statusBar != null) { - statusBar.updatePipVisibility(visible); - } + private void updatePipVisibility(final boolean visible) { + SystemServicesProxy.getInstance(mContext).setTvPipVisibility(visible); } } diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java index 3a82f8828b6d..ff934ef18677 100644 --- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java +++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java @@ -147,11 +147,12 @@ public class WallpaperBackupAgent extends BackupAgent { } // only back up the wallpapers if we've been told they're eligible - if ((sysEligible || lockEligible) && mWallpaperInfo.exists()) { + if (mWallpaperInfo.exists()) { if (sysChanged || lockChanged || !infoStage.exists()) { if (DEBUG) Slog.v(TAG, "New wallpaper configuration; copying"); FileUtils.copyFileOrThrow(mWallpaperInfo, infoStage); } + if (DEBUG) Slog.v(TAG, "Storing wallpaper metadata"); fullBackupFile(infoStage, data); } if (sysEligible && mWallpaperFile.exists()) { @@ -159,6 +160,7 @@ public class WallpaperBackupAgent extends BackupAgent { if (DEBUG) Slog.v(TAG, "New system wallpaper; copying"); FileUtils.copyFileOrThrow(mWallpaperFile, imageStage); } + if (DEBUG) Slog.v(TAG, "Storing system wallpaper image"); fullBackupFile(imageStage, data); prefs.edit().putInt(SYSTEM_GENERATION, sysGeneration).apply(); } @@ -169,6 +171,7 @@ public class WallpaperBackupAgent extends BackupAgent { if (DEBUG) Slog.v(TAG, "New lock wallpaper; copying"); FileUtils.copyFileOrThrow(mLockWallpaperFile, lockImageStage); } + if (DEBUG) Slog.v(TAG, "Storing lock wallpaper image"); fullBackupFile(lockImageStage, data); prefs.edit().putInt(LOCK_GENERATION, lockGeneration).apply(); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java index 562d95065cd8..582b19b62c22 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java @@ -341,6 +341,8 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen mDoubleTapDetected = false; mSecondFingerDoubleTap = false; mGestureStarted = false; + mGestureDetector.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_CANCEL, + 0.0f, 0.0f, 0)); cancelGesture(); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 8dca14f13202..e00178f8ea41 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -3683,13 +3683,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { Rect boundsInScreen = mTempRect; focus.getBoundsInScreen(boundsInScreen); - // Clip to the window bounds. - Rect windowBounds = mTempRect1; - getWindowBounds(focus.getWindowId(), windowBounds); - if (!boundsInScreen.intersect(windowBounds)) { - return false; - } - // Apply magnification if needed. MagnificationSpec spec = getCompatibleMagnificationSpecLocked(focus.getWindowId()); if (spec != null && !spec.isNop()) { @@ -3697,6 +3690,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { boundsInScreen.scale(1 / spec.scale); } + // Clip to the window bounds. + Rect windowBounds = mTempRect1; + getWindowBounds(focus.getWindowId(), windowBounds); + if (!boundsInScreen.intersect(windowBounds)) { + return false; + } + // Clip to the screen bounds. Point screenSize = mTempPoint; mDefaultDisplay.getRealSize(screenSize); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index ae61a7e10dd0..a032dcbeee6f 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -372,6 +372,11 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final int EVENT_SET_ACCEPT_UNVALIDATED = 28; /** + * used to specify whether a network should not be penalized when it becomes unvalidated. + */ + private static final int EVENT_SET_AVOID_UNVALIDATED = 35; + + /** * used to ask the user to confirm a connection to an unvalidated network. * obj = network */ @@ -2070,7 +2075,9 @@ public class ConnectivityService extends IConnectivityManager.Stub mKeepaliveTracker.dump(pw); pw.println(); + dumpAvoidBadWifiSettings(pw); + pw.println(); if (mInetLog != null && mInetLog.size() > 0) { pw.println(); pw.println("Inet condition reports:"); @@ -2712,6 +2719,12 @@ public class ConnectivityService extends IConnectivityManager.Stub accept ? 1 : 0, always ? 1: 0, network)); } + @Override + public void setAvoidUnvalidated(Network network) { + enforceConnectivityInternalPermission(); + mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_AVOID_UNVALIDATED, network)); + } + private void handleSetAcceptUnvalidated(Network network, boolean accept, boolean always) { if (DBG) log("handleSetAcceptUnvalidated network=" + network + " accept=" + accept + " always=" + always); @@ -2752,6 +2765,20 @@ public class ConnectivityService extends IConnectivityManager.Stub } + private void handleSetAvoidUnvalidated(Network network) { + NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); + if (nai == null || nai.lastValidated) { + // Nothing to do. The network either disconnected or revalidated. + return; + } + if (!nai.avoidUnvalidated) { + int oldScore = nai.getCurrentScore(); + nai.avoidUnvalidated = true; + rematchAllNetworksAndRequests(nai, oldScore); + sendUpdatedScoreToFactories(nai); + } + } + private void scheduleUnvalidatedPrompt(NetworkAgentInfo nai) { if (VDBG) log("scheduleUnvalidatedPrompt " + nai.network); mHandler.sendMessageDelayed( @@ -2759,31 +2786,70 @@ public class ConnectivityService extends IConnectivityManager.Stub PROMPT_UNVALIDATED_DELAY_MS); } - private boolean mAvoidBadWifi; + private boolean mAvoidBadWifi = true; public boolean avoidBadWifi() { return mAvoidBadWifi; } @VisibleForTesting - public boolean updateAvoidBadWifi() { - // There are two modes: either we always automatically avoid unvalidated wifi, or we show a - // dialog and don't switch to it. The behaviour is controlled by the NETWORK_AVOID_BAD_WIFI - // setting. If the setting has no value, then the value is taken from the config value, - // which can be changed via OEM/carrier overlays. - // - // The only valid values for NETWORK_AVOID_BAD_WIFI are null and unset. Currently, the unit - // test uses 0 in order to avoid having to mock out fetching the carrier setting. - int defaultAvoidBadWifi = - mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi); - int avoid = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.NETWORK_AVOID_BAD_WIFI, defaultAvoidBadWifi); + /** Whether the device or carrier configuration disables avoiding bad wifi by default. */ + public boolean configRestrictsAvoidBadWifi() { + return mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi) == 0; + } + + /** Whether we should display a notification when wifi becomes unvalidated. */ + public boolean shouldNotifyWifiUnvalidated() { + return configRestrictsAvoidBadWifi() && + Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.NETWORK_AVOID_BAD_WIFI) == null; + } + + private boolean updateAvoidBadWifi() { + boolean settingAvoidBadWifi = "1".equals(Settings.Global.getString( + mContext.getContentResolver(), Settings.Global.NETWORK_AVOID_BAD_WIFI)); boolean prev = mAvoidBadWifi; - mAvoidBadWifi = (avoid == 1); + mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi(); return mAvoidBadWifi != prev; } + private void dumpAvoidBadWifiSettings(IndentingPrintWriter pw) { + boolean configRestrict = configRestrictsAvoidBadWifi(); + if (!configRestrict) { + pw.println("Bad Wi-Fi avoidance: unrestricted"); + return; + } + + pw.println("Bad Wi-Fi avoidance: " + avoidBadWifi()); + pw.increaseIndent(); + pw.println("Config restrict: " + configRestrict); + + String value = Settings.Global.getString( + mContext.getContentResolver(), Settings.Global.NETWORK_AVOID_BAD_WIFI); + String description; + // Can't use a switch statement because strings are legal case labels, but null is not. + if ("0".equals(value)) { + description = "get stuck"; + } else if (value == null) { + description = "prompt"; + } else if ("1".equals(value)) { + description = "avoid"; + } else { + description = value + " (?)"; + } + pw.println("User setting: " + description); + pw.println("Network overrides:"); + pw.increaseIndent(); + for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { + if (nai.avoidUnvalidated) { + pw.println(nai.name()); + } + } + pw.decreaseIndent(); + pw.decreaseIndent(); + } + private void showValidationNotification(NetworkAgentInfo nai, NotificationType type) { final String action; switch (type) { @@ -2833,7 +2899,7 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkCapabilities nc = nai.networkCapabilities; if (DBG) log("handleNetworkUnvalidated " + nai.name() + " cap=" + nc); - if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && !avoidBadWifi()) { + if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && shouldNotifyWifiUnvalidated()) { showValidationNotification(nai, NotificationType.LOST_INTERNET); } } @@ -2911,6 +2977,10 @@ public class ConnectivityService extends IConnectivityManager.Stub handleSetAcceptUnvalidated((Network) msg.obj, msg.arg1 != 0, msg.arg2 != 0); break; } + case EVENT_SET_AVOID_UNVALIDATED: { + handleSetAvoidUnvalidated((Network) msg.obj); + break; + } case EVENT_PROMPT_UNVALIDATED: { handlePromptUnvalidated((Network) msg.obj); break; diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 8ae491707542..df1b6f51bc33 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -3077,8 +3077,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) { Slog.d(TAG, "Found an input method " + p); } - } catch (XmlPullParserException | IOException e) { - Slog.w(TAG, "Unable to load input method " + compName, e); + } catch (Exception e) { + Slog.wtf(TAG, "Unable to load input method " + compName, e); } } diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java index 383e25a6d293..2ff036be4b4b 100644 --- a/services/core/java/com/android/server/ServiceWatcher.java +++ b/services/core/java/com/android/server/ServiceWatcher.java @@ -141,8 +141,12 @@ public class ServiceWatcher implements ServiceConnection { * <p> * Note that if there are no matching encryption-aware services, we may not * bind to a real service until after the current user is unlocked. + * + * @returns {@code true} if a potential service implementation was found. */ public boolean start() { + if (isServiceMissing()) return false; + synchronized (mLock) { bindBestPackageLocked(mServicePackageName, false); } @@ -174,6 +178,17 @@ public class ServiceWatcher implements ServiceConnection { } /** + * Check if any instance of this service is present on the device, + * regardless of it being encryption-aware or not. + */ + private boolean isServiceMissing() { + final Intent intent = new Intent(mAction); + final int flags = PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; + return mPm.queryIntentServicesAsUser(intent, flags, mCurrentUserId).isEmpty(); + } + + /** * Searches and binds to the best package, or do nothing if the best package * is already bound, unless force rebinding is requested. * @@ -181,7 +196,7 @@ public class ServiceWatcher implements ServiceConnection { * packages if it is {@code null}. * @param forceRebind Force a rebinding to the best package if it's already * bound. - * @return {@code true} if a valid package was found to bind to. + * @returns {@code true} if a valid package was found to bind to. */ private boolean bindBestPackageLocked(String justCheckThisPackage, boolean forceRebind) { Intent intent = new Intent(mAction); diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java index 4b0d4be11b14..4f02a23d1e76 100644 --- a/services/core/java/com/android/server/TextServicesManagerService.java +++ b/services/core/java/com/android/server/TextServicesManagerService.java @@ -459,71 +459,75 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { if (!calledFromValidUser()) { return null; } + final int subtypeHashCode; + final SpellCheckerInfo sci; + final Locale systemLocale; synchronized (mSpellCheckerMap) { - final int subtypeHashCode = + subtypeHashCode = mSettings.getSelectedSpellCheckerSubtype(SpellCheckerSubtype.SUBTYPE_ID_NONE); if (DBG) { Slog.w(TAG, "getCurrentSpellCheckerSubtype: " + subtypeHashCode); } - final SpellCheckerInfo sci = getCurrentSpellChecker(null); - if (sci == null || sci.getSubtypeCount() == 0) { - if (DBG) { - Slog.w(TAG, "Subtype not found."); + sci = getCurrentSpellChecker(null); + systemLocale = mContext.getResources().getConfiguration().locale; + } + if (sci == null || sci.getSubtypeCount() == 0) { + if (DBG) { + Slog.w(TAG, "Subtype not found."); + } + return null; + } + if (subtypeHashCode == SpellCheckerSubtype.SUBTYPE_ID_NONE + && !allowImplicitlySelectedSubtype) { + return null; + } + String candidateLocale = null; + if (subtypeHashCode == 0) { + // Spell checker language settings == "auto" + final InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); + if (imm != null) { + final InputMethodSubtype currentInputMethodSubtype = + imm.getCurrentInputMethodSubtype(); + if (currentInputMethodSubtype != null) { + final String localeString = currentInputMethodSubtype.getLocale(); + if (!TextUtils.isEmpty(localeString)) { + // 1. Use keyboard locale if available in the spell checker + candidateLocale = localeString; + } } - return null; } - if (subtypeHashCode == SpellCheckerSubtype.SUBTYPE_ID_NONE - && !allowImplicitlySelectedSubtype) { - return null; + if (candidateLocale == null) { + // 2. Use System locale if available in the spell checker + candidateLocale = systemLocale.toString(); } - String candidateLocale = null; + } + SpellCheckerSubtype candidate = null; + for (int i = 0; i < sci.getSubtypeCount(); ++i) { + final SpellCheckerSubtype scs = sci.getSubtypeAt(i); if (subtypeHashCode == 0) { - // Spell checker language settings == "auto" - final InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); - if (imm != null) { - final InputMethodSubtype currentInputMethodSubtype = - imm.getCurrentInputMethodSubtype(); - if (currentInputMethodSubtype != null) { - final String localeString = currentInputMethodSubtype.getLocale(); - if (!TextUtils.isEmpty(localeString)) { - // 1. Use keyboard locale if available in the spell checker - candidateLocale = localeString; - } + final String scsLocale = scs.getLocale(); + if (candidateLocale.equals(scsLocale)) { + return scs; + } else if (candidate == null) { + if (candidateLocale.length() >= 2 && scsLocale.length() >= 2 + && candidateLocale.startsWith(scsLocale)) { + // Fall back to the applicable language + candidate = scs; } } - if (candidateLocale == null) { - // 2. Use System locale if available in the spell checker - candidateLocale = mContext.getResources().getConfiguration().locale.toString(); - } - } - SpellCheckerSubtype candidate = null; - for (int i = 0; i < sci.getSubtypeCount(); ++i) { - final SpellCheckerSubtype scs = sci.getSubtypeAt(i); - if (subtypeHashCode == 0) { - final String scsLocale = scs.getLocale(); - if (candidateLocale.equals(scsLocale)) { - return scs; - } else if (candidate == null) { - if (candidateLocale.length() >= 2 && scsLocale.length() >= 2 - && candidateLocale.startsWith(scsLocale)) { - // Fall back to the applicable language - candidate = scs; - } - } - } else if (scs.hashCode() == subtypeHashCode) { - if (DBG) { - Slog.w(TAG, "Return subtype " + scs.hashCode() + ", input= " + locale - + ", " + scs.getLocale()); - } - // 3. Use the user specified spell check language - return scs; + } else if (scs.hashCode() == subtypeHashCode) { + if (DBG) { + Slog.w(TAG, "Return subtype " + scs.hashCode() + ", input= " + locale + + ", " + scs.getLocale()); } + // 3. Use the user specified spell check language + return scs; } - // 4. Fall back to the applicable language and return it if not null - // 5. Simply just return it even if it's null which means we could find no suitable - // spell check languages - return candidate; } + // 4. Fall back to the applicable language and return it if not null + // 5. Simply just return it even if it's null which means we could find no suitable + // spell check languages + return candidate; } @Override diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index b69750794761..82605c62472d 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -1562,8 +1562,10 @@ public class AccountManagerService /* * Database transaction was successful. Clean up cached * data associated with the account in the user profile. + * The account is now being tracked for remote access. */ - insertAccountIntoCacheLocked(accounts, renamedAccount); + renamedAccount = insertAccountIntoCacheLocked(accounts, renamedAccount); + /* * Extract the data and token caches before removing the * old account to preserve the user data associated with @@ -5701,16 +5703,22 @@ public class AccountManagerService /** * This assumes that the caller has already checked that the account is not already present. + * IMPORTANT: The account being inserted will begin to be tracked for access in remote + * processes and if you will return this account to apps you should return the result. + * @return The inserted account which is a new instance that is being tracked. */ - private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) { + private Account insertAccountIntoCacheLocked(UserAccounts accounts, Account account) { Account[] accountsForType = accounts.accountCache.get(account.type); int oldLength = (accountsForType != null) ? accountsForType.length : 0; Account[] newAccountsForType = new Account[oldLength + 1]; if (accountsForType != null) { System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength); } - newAccountsForType[oldLength] = new Account(account, new AccountAccessTracker()); + IAccountAccessTracker accessTracker = account.getAccessTracker() != null + ? account.getAccessTracker() : new AccountAccessTracker(); + newAccountsForType[oldLength] = new Account(account, accessTracker); accounts.accountCache.put(account.type, newAccountsForType); + return newAccountsForType[oldLength]; } private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered, diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index f65fab8d98aa..91f493c98e2d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -3847,16 +3847,19 @@ public final class ActivityManagerService extends ActivityManagerNative app.killed = false; app.killedByAm = false; checkTime(startTime, "startProcess: starting to update pids map"); + ProcessRecord oldApp; + synchronized (mPidsSelfLocked) { + oldApp = mPidsSelfLocked.get(startResult.pid); + } + // If there is already an app occupying that pid that hasn't been cleaned up + if (oldApp != null && !app.isolated) { + // Clean up anything relating to this pid first + Slog.w(TAG, "Reusing pid " + startResult.pid + + " while app is still mapped to it"); + cleanUpApplicationRecordLocked(oldApp, false, false, -1, + true /*replacingPid*/); + } synchronized (mPidsSelfLocked) { - ProcessRecord oldApp; - // If there is already an app occupying that pid that hasn't been cleaned up - if ((oldApp = mPidsSelfLocked.get(startResult.pid)) != null && !app.isolated) { - // Clean up anything relating to this pid first - Slog.w(TAG, "Reusing pid " + startResult.pid - + " while app is still mapped to it"); - cleanUpApplicationRecordLocked(oldApp, false, false, -1, - true /*replacingPid*/); - } this.mPidsSelfLocked.put(startResult.pid, app); if (isActivityProcess) { Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG); diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index cb4bb8840b5a..2a618bcc2eac 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -140,12 +140,14 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { public boolean everValidated; // The result of the last validation attempt on this network (true if validated, false if not). - // This bit exists only because we never unvalidate a network once it's been validated, and that - // is because the network scoring and revalidation code does not (may not?) deal properly with - // networks becoming unvalidated. - // TODO: Fix the network scoring code, remove this, and rename everValidated to validated. public boolean lastValidated; + // If true, becoming unvalidated will lower the network's score. This is only meaningful if the + // system is configured not to do this for certain networks, e.g., if the + // config_networkAvoidBadWifi option is set to 0 and the user has not overridden that via + // Settings.Global.NETWORK_AVOID_BAD_WIFI. + public boolean avoidUnvalidated; + // Whether a captive portal was ever detected on this network. // This is a sticky bit; once set it is never cleared. public boolean everCaptivePortalDetected; @@ -426,8 +428,10 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // Return true on devices configured to ignore score penalty for wifi networks // that become unvalidated (b/31075769). private boolean ignoreWifiUnvalidationPenalty() { - boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI); - return isWifi && !mConnService.avoidBadWifi() && everValidated; + boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && + networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + boolean avoidBadWifi = mConnService.avoidBadWifi() || avoidUnvalidated; + return isWifi && !avoidBadWifi && everValidated; } // Get the current score for this Network. This may be modified from what the diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java index 7d1da01b8ca6..58c76ec7674a 100644 --- a/services/core/java/com/android/server/connectivity/PacManager.java +++ b/services/core/java/com/android/server/connectivity/PacManager.java @@ -27,6 +27,7 @@ import android.content.ServiceConnection; import android.net.ProxyInfo; import android.net.Uri; import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; @@ -39,10 +40,10 @@ import com.android.internal.annotations.GuardedBy; import com.android.net.IProxyCallback; import com.android.net.IProxyPortListener; import com.android.net.IProxyService; -import com.android.server.IoThread; import libcore.io.Streams; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URL; import java.net.URLConnection; @@ -66,6 +67,7 @@ public class PacManager { private static final int DELAY_1 = 0; private static final int DELAY_4 = 3; private static final int DELAY_LONG = 4; + private static final long MAX_PAC_SIZE = 20 * 1000 * 1000; /** Keep these values up-to-date with ProxyService.java */ public static final String KEY_PROXY = "keyProxy"; @@ -123,15 +125,21 @@ public class PacManager { } }; + private final HandlerThread mNetThread = new HandlerThread("android.pacmanager", + android.os.Process.THREAD_PRIORITY_DEFAULT); + private final Handler mNetThreadHandler; + class PacRefreshIntentReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { - IoThread.getHandler().post(mPacDownloader); + mNetThreadHandler.post(mPacDownloader); } } public PacManager(Context context, Handler handler, int proxyMessage) { mContext = context; mLastPort = -1; + mNetThread.start(); + mNetThreadHandler = new Handler(mNetThread.getLooper()); mPacRefreshIntent = PendingIntent.getBroadcast( context, 0, new Intent(ACTION_PAC_REFRESH), 0); @@ -199,7 +207,25 @@ public class PacManager { private static String get(Uri pacUri) throws IOException { URL url = new URL(pacUri.toString()); URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY); - return new String(Streams.readFully(urlConnection.getInputStream())); + long contentLength = -1; + try { + contentLength = Long.parseLong(urlConnection.getHeaderField("Content-Length")); + } catch (NumberFormatException e) { + // Ignore + } + if (contentLength > MAX_PAC_SIZE) { + throw new IOException("PAC too big: " + contentLength + " bytes"); + } + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int count; + while ((count = urlConnection.getInputStream().read(buffer)) != -1) { + bytes.write(buffer, 0, count); + if (bytes.size() > MAX_PAC_SIZE) { + throw new IOException("PAC too big"); + } + } + return bytes.toString(); } private int getNextDelay(int currentDelay) { @@ -267,7 +293,7 @@ public class PacManager { intent.setClassName(PAC_PACKAGE, PAC_SERVICE); if ((mProxyConnection != null) && (mConnection != null)) { // Already bound no need to bind again, just download the new file. - IoThread.getHandler().post(mPacDownloader); + mNetThreadHandler.post(mPacDownloader); return; } mConnection = new ServiceConnection() { @@ -297,7 +323,7 @@ public class PacManager { } catch (RemoteException e) { Log.e(TAG, "Unable to reach ProxyService - PAC will not be started", e); } - IoThread.getHandler().post(mPacDownloader); + mNetThreadHandler.post(mPacDownloader); } } } diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java index a63399656bab..9fa93f4b8a6c 100644 --- a/services/core/java/com/android/server/dreams/DreamController.java +++ b/services/core/java/com/android/server/dreams/DreamController.java @@ -26,6 +26,7 @@ import android.content.ServiceConnection; import android.os.Binder; import android.os.Handler; import android.os.IBinder; +import android.os.PowerManager; import android.os.RemoteException; import android.os.IBinder.DeathRecipient; import android.os.SystemClock; @@ -116,19 +117,19 @@ final class DreamController { } public void startDream(Binder token, ComponentName name, - boolean isTest, boolean canDoze, int userId) { + boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) { stopDream(true /*immediate*/); Trace.traceBegin(Trace.TRACE_TAG_POWER, "startDream"); try { - // Close the notification shade. Don't need to send to all, but better to be explicit. + // Close the notification shade. No need to send to all, but better to be explicit. mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL); Slog.i(TAG, "Starting dream: name=" + name + ", isTest=" + isTest + ", canDoze=" + canDoze + ", userId=" + userId); - mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId); + mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId, wakeLock); mDreamStartTime = SystemClock.elapsedRealtime(); MetricsLogger.visible(mContext, @@ -230,6 +231,7 @@ final class DreamController { if (oldDream.mBound) { mContext.unbindService(oldDream); } + oldDream.releaseWakeLockIfNeeded(); try { mIWindowManager.removeWindowToken(oldDream.mToken); @@ -280,6 +282,7 @@ final class DreamController { public final boolean mCanDoze; public final int mUserId; + public PowerManager.WakeLock mWakeLock; public boolean mBound; public boolean mConnected; public IDreamService mService; @@ -288,12 +291,17 @@ final class DreamController { public boolean mWakingGently; public DreamRecord(Binder token, ComponentName name, - boolean isTest, boolean canDoze, int userId) { + boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) { mToken = token; mName = name; mIsTest = isTest; mCanDoze = canDoze; mUserId = userId; + mWakeLock = wakeLock; + // Hold the lock while we're waiting for the service to connect. Released either when + // DreamService connects (and is then responsible for keeping the device awake) or + // dreaming stops. + mWakeLock.acquire(); } // May be called on any thread. @@ -316,14 +324,25 @@ final class DreamController { mHandler.post(new Runnable() { @Override public void run() { - mConnected = true; - if (mCurrentDream == DreamRecord.this && mService == null) { - attach(IDreamService.Stub.asInterface(service)); + try { + mConnected = true; + if (mCurrentDream == DreamRecord.this && mService == null) { + attach(IDreamService.Stub.asInterface(service)); + } + } finally { + releaseWakeLockIfNeeded(); } } }); } + private void releaseWakeLockIfNeeded() { + if (mWakeLock != null) { + mWakeLock.release(); + mWakeLock = null; + } + } + // May be called on any thread. @Override public void onServiceDisconnected(ComponentName name) { diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index a783fa25ed45..20bccf19f131 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -370,12 +370,10 @@ public final class DreamManagerService extends SystemService { mCurrentDreamCanDoze = canDoze; mCurrentDreamUserId = userId; - mHandler.post(new Runnable() { - @Override - public void run() { - mController.startDream(newToken, name, isTest, canDoze, userId); - } - }); + PowerManager.WakeLock wakeLock = mPowerManager + .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "startDream"); + mHandler.post(wakeLock.wrap( + () -> mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock))); } private void stopDreamLocked(final boolean immediate) { diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 697186e562fb..34f6aa7f73ab 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -767,6 +767,13 @@ public class MediaSessionService extends SystemService implements Monitor { + "setup is in progress."); return; } + if (isGlobalPriorityActive() && uid != Process.SYSTEM_UID) { + // Prevent dispatching key event through reflection while the global priority + // session is active. + Slog.i(TAG, "Only the system can dispatch media key event " + + "to the global priority session."); + return; + } synchronized (mLock) { // If we don't have a media button receiver to fall back on diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index a57345e7fa4f..d479bfc181da 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -2723,9 +2723,31 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + /** + * Toggle the firewall standby chain and inform listeners if the uid rules have effectively + * changed. + */ void updateRulesForAppIdleParoleUL() { - boolean enableChain = !mUsageStats.isAppIdleParoleOn(); + boolean paroled = mUsageStats.isAppIdleParoleOn(); + boolean enableChain = !paroled; enableFirewallChainUL(FIREWALL_CHAIN_STANDBY, enableChain); + + int ruleCount = mUidFirewallStandbyRules.size(); + for (int i = 0; i < ruleCount; i++) { + int uid = mUidFirewallStandbyRules.keyAt(i); + int oldRules = mUidRules.get(uid); + if (enableChain) { + // Chain wasn't enabled before and the other power-related + // chains are whitelists, so we can clear the + // MASK_ALL_NETWORKS part of the rules and re-inform listeners if + // the effective rules result in blocking network access. + oldRules &= MASK_METERED_NETWORKS; + } else { + // Skip if it had no restrictions to begin with + if ((oldRules & MASK_ALL_NETWORKS) == 0) continue; + } + updateRulesForPowerRestrictionsUL(uid, oldRules, paroled); + } } /** @@ -3079,14 +3101,34 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * <strong>NOTE: </strong>This method does not update the firewall rules on {@code netd}. */ private void updateRulesForPowerRestrictionsUL(int uid) { + final int oldUidRules = mUidRules.get(uid, RULE_NONE); + + final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules, false); + + if (newUidRules == RULE_NONE) { + mUidRules.delete(uid); + } else { + mUidRules.put(uid, newUidRules); + } + } + + /** + * Similar to above but ignores idle state if app standby is currently disabled by parole. + * + * @param uid the uid of the app to update rules for + * @param oldUidRules the current rules for the uid, in order to determine if there's a change + * @param paroled whether to ignore idle state of apps and only look at other restrictions. + * + * @return the new computed rules for the uid + */ + private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules, boolean paroled) { if (!isUidValidForBlacklistRules(uid)) { if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid); - return; + return RULE_NONE; } - final boolean isIdle = isUidIdle(uid); + final boolean isIdle = !paroled && isUidIdle(uid); final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode; - final int oldUidRules = mUidRules.get(uid, RULE_NONE); final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid); final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid); @@ -3120,12 +3162,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + ", oldUidRules=" + uidRulesToString(oldUidRules)); } - if (newUidRules == RULE_NONE) { - mUidRules.delete(uid); - } else { - mUidRules.put(uid, newUidRules); - } - // Second step: notify listeners if state changed. if (newRule != oldRule) { if (newRule == RULE_NONE || (newRule & RULE_ALLOW_ALL) != 0) { @@ -3142,6 +3178,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget(); } + + return newUidRules; } private class AppIdleStateChangeListener diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index 42079fb0df1b..689917cd670a 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -172,6 +172,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub { Log.i(TAG, "Cleaning up OTA Dexopt state."); } mDexoptCommands = null; + availableSpaceAfterDexopt = getAvailableSpace(); performMetricsLogging(); } diff --git a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java index c764833e08a6..9bf04768ba1a 100644 --- a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java +++ b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java @@ -25,7 +25,9 @@ import android.content.Intent; import android.content.IntentFilter; import android.graphics.PixelFormat; import android.graphics.drawable.ColorDrawable; +import android.os.Binder; import android.os.Handler; +import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; @@ -36,7 +38,6 @@ import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; import android.util.DisplayMetrics; import android.util.Slog; -import android.util.SparseBooleanArray; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; @@ -66,6 +67,7 @@ public class ImmersiveModeConfirmation { private final H mHandler; private final long mShowDelayMs; private final long mPanicThresholdMs; + private final IBinder mWindowToken = new Binder(); private boolean mConfirmed; private ClingWindowView mClingWindow; @@ -190,13 +192,13 @@ public class ImmersiveModeConfirmation { WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, 0 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED , PixelFormat.TRANSLUCENT); lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; lp.setTitle("ImmersiveModeConfirmation"); lp.windowAnimations = com.android.internal.R.style.Animation_ImmersiveModeConfirmation; + lp.token = getWindowToken(); return lp; } @@ -208,6 +210,13 @@ public class ImmersiveModeConfirmation { Gravity.CENTER_HORIZONTAL | Gravity.TOP); } + /** + * @return the window token that's used by all ImmersiveModeConfirmation windows. + */ + public IBinder getWindowToken() { + return mWindowToken; + } + private class ClingWindowView extends FrameLayout { private static final int BGCOLOR = 0x80000000; private static final int OFFSET_DP = 96; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index ccf9b898d7b2..7162b9466a56 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3852,11 +3852,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { }; @Override + public void setRecentsVisibilityLw(boolean visible) { + mRecentsVisible = visible; + } + + @Override + public void setTvPipVisibilityLw(boolean visible) { + mTvPictureInPictureVisible = visible; + } + + @Override public int adjustSystemUiVisibilityLw(int visibility) { mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); - mRecentsVisible = (visibility & View.RECENT_APPS_VISIBLE) > 0; - mTvPictureInPictureVisible = (visibility & View.TV_PICTURE_IN_PICTURE_VISIBLE) > 0; // Reset any bits in mForceClearingStatusBarVisibility that // are now clear. @@ -7429,11 +7437,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { private int updateSystemUiVisibilityLw() { // If there is no window focused, there will be nobody to handle the events // anyway, so just hang on in whatever state we're in until things settle down. - final WindowState win = mFocusedWindow != null ? mFocusedWindow + WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow : mTopFullscreenOpaqueWindowState; - if (win == null) { + if (winCandidate == null) { return 0; } + if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) { + // The immersive mode confirmation should never affect the system bar visibility, + // otherwise it will unhide the navigation bar and hide itself. + winCandidate = isStatusBarKeyguard() ? mStatusBar : mTopFullscreenOpaqueWindowState; + if (winCandidate == null) { + return 0; + } + } + final WindowState win = winCandidate; if ((win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 && mHideLockScreen == true) { // We are updating at a point where the keyguard has gotten // focus, but we were last in a state where the top window is diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 536e646368d0..5f6800a8d0f2 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -89,6 +89,7 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; import com.android.server.EventLogTags; +import com.android.server.FgThread; import com.android.server.SystemService; import libcore.io.IoUtils; @@ -589,6 +590,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { class WallpaperConnection extends IWallpaperConnection.Stub implements ServiceConnection { + + /** Time in milliseconds until we expect the wallpaper to reconnect (unless we're in the + * middle of an update). If exceeded, the wallpaper gets reset to the system default. */ + private static final long WALLPAPER_RECONNECT_TIMEOUT_MS = 5000; + final WallpaperInfo mInfo; final Binder mToken = new Binder(); IWallpaperService mService; @@ -599,6 +605,18 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { boolean mDimensionsChanged = false; boolean mPaddingChanged = false; + private Runnable mResetRunnable = () -> { + synchronized (mLock) { + if (!mWallpaper.wallpaperUpdating + && mWallpaper.userId == mCurrentUserId) { + Slog.w(TAG, "Wallpaper reconnect timed out, " + + "reverting to built-in wallpaper!"); + clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, + null); + } + } + }; + public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) { mInfo = info; mWallpaper = wallpaper; @@ -615,6 +633,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { // locking there and anyway we always need to be able to // recover if there is something wrong. saveSettingsLocked(mWallpaper.userId); + FgThread.getHandler().removeCallbacks(mResetRunnable); } } } @@ -641,6 +660,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null); } else { mWallpaper.lastDiedTime = SystemClock.uptimeMillis(); + + // If we didn't reset it right away, do so after we couldn't connect to + // it for an extended amount of time to avoid having a black wallpaper. + FgThread.getHandler().removeCallbacks(mResetRunnable); + FgThread.getHandler().postDelayed(mResetRunnable, + WALLPAPER_RECONNECT_TIMEOUT_MS); } final String flattened = name.flattenToString(); EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED, @@ -752,6 +777,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { if (wallpaper.wallpaperComponent != null && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) { wallpaper.wallpaperUpdating = true; + if (wallpaper.connection != null) { + FgThread.getHandler().removeCallbacks( + wallpaper.connection.mResetRunnable); + } } } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 707b13780c46..1b2322bd1903 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -10227,6 +10227,32 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void setRecentsVisibility(boolean visible) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Caller does not hold permission " + + android.Manifest.permission.STATUS_BAR); + } + + synchronized (mWindowMap) { + mPolicy.setRecentsVisibilityLw(visible); + } + } + + @Override + public void setTvPipVisibility(boolean visible) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Caller does not hold permission " + + android.Manifest.permission.STATUS_BAR); + } + + synchronized (mWindowMap) { + mPolicy.setTvPipVisibilityLw(visible); + } + } + + @Override public void statusBarVisibilityChanged(int visibility) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) != PackageManager.PERMISSION_GRANTED) { diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java index 6d9020390bef..215059d563f6 100644 --- a/services/net/java/android/net/ip/IpManager.java +++ b/services/net/java/android/net/ip/IpManager.java @@ -45,6 +45,7 @@ import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.IState; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.server.net.NetlinkTracker; @@ -395,6 +396,7 @@ public class IpManager extends StateMachine { private final WakeupMessage mProvisioningTimeoutAlarm; private final WakeupMessage mDhcpActionTimeoutAlarm; private final LocalLog mLocalLog; + private final MessageHandlingLogger mMsgStateLogger; private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); private NetworkInterface mNetworkInterface; @@ -482,6 +484,7 @@ public class IpManager extends StateMachine { setInitialState(mStoppedState); mLocalLog = new LocalLog(MAX_LOG_RECORDS); + mMsgStateLogger = new MessageHandlingLogger(); super.start(); } @@ -591,9 +594,9 @@ public class IpManager extends StateMachine { @Override protected String getLogRecString(Message msg) { final String logLine = String.format( - "%s/%d %d %d %s", + "%s/%d %d %d %s [%s]", mInterfaceName, mNetworkInterface == null ? -1 : mNetworkInterface.getIndex(), - msg.arg1, msg.arg2, Objects.toString(msg.obj)); + msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger); final String richerLogLine = getWhatToString(msg.what) + " " + logLine; mLocalLog.log(richerLogLine); @@ -601,6 +604,7 @@ public class IpManager extends StateMachine { Log.d(mTag, richerLogLine); } + mMsgStateLogger.reset(); return logLine; } @@ -609,7 +613,11 @@ public class IpManager extends StateMachine { // Don't log EVENT_NETLINK_LINKPROPERTIES_CHANGED. They can be noisy, // and we already log any LinkProperties change that results in an // invocation of IpManager.Callback#onLinkPropertiesChange(). - return (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED); + final boolean shouldLog = (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED); + if (!shouldLog) { + mMsgStateLogger.reset(); + } + return shouldLog; } private void getNetworkInterface() { @@ -965,7 +973,6 @@ public class IpManager extends StateMachine { } } - class StoppedState extends State { @Override public void enter() { @@ -1015,6 +1022,8 @@ public class IpManager extends StateMachine { default: return NOT_HANDLED; } + + mMsgStateLogger.handled(this, getCurrentState()); return HANDLED; } } @@ -1031,6 +1040,13 @@ public class IpManager extends StateMachine { @Override public boolean processMessage(Message msg) { switch (msg.what) { + case CMD_STOP: + break; + + case DhcpClient.CMD_CLEAR_LINKADDRESS: + clearIPv4Address(); + break; + case DhcpClient.CMD_ON_QUIT: mDhcpClient = null; transitionTo(mStoppedState); @@ -1039,6 +1055,8 @@ public class IpManager extends StateMachine { default: deferMessage(msg); } + + mMsgStateLogger.handled(this, getCurrentState()); return HANDLED; } } @@ -1095,6 +1113,8 @@ public class IpManager extends StateMachine { // is EVENT_NETLINK_LINKPROPERTIES_CHANGED (handled above). deferMessage(msg); } + + mMsgStateLogger.handled(this, getCurrentState()); return HANDLED; } @@ -1302,7 +1322,29 @@ public class IpManager extends StateMachine { default: return NOT_HANDLED; } + + mMsgStateLogger.handled(this, getCurrentState()); return HANDLED; } } + + private static class MessageHandlingLogger { + public String processedInState; + public String receivedInState; + + public void reset() { + processedInState = null; + receivedInState = null; + } + + public void handled(State processedIn, IState receivedIn) { + processedInState = processedIn.getClass().getSimpleName(); + receivedInState = receivedIn.getName(); + } + + public String toString() { + return String.format("rcvd_in=%s, proc_in=%s", + receivedInState, processedInState); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java index ace6c6e86a66..24243a09de90 100644 --- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java @@ -82,6 +82,7 @@ import com.android.server.net.NetworkPinner; import java.net.InetAddress; import java.util.ArrayList; +import java.util.Arrays; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; @@ -601,6 +602,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { private class WrappedConnectivityService extends ConnectivityService { private WrappedNetworkMonitor mLastCreatedNetworkMonitor; + public boolean configRestrictsAvoidBadWifi; public WrappedConnectivityService(Context context, INetworkManagementService netManager, INetworkStatsService statsService, INetworkPolicyManager policyManager, @@ -656,6 +658,11 @@ public class ConnectivityServiceTest extends AndroidTestCase { return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj); } + @Override + public boolean configRestrictsAvoidBadWifi() { + return configRestrictsAvoidBadWifi; + } + public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() { return mLastCreatedNetworkMonitor; } @@ -2035,8 +2042,48 @@ public class ConnectivityServiceTest extends AndroidTestCase { @SmallTest public void testAvoidBadWifiSetting() throws Exception { + final ContentResolver cr = mServiceContext.getContentResolver(); + final String settingName = Settings.Global.NETWORK_AVOID_BAD_WIFI; + + mService.configRestrictsAvoidBadWifi = false; + String[] values = new String[] {null, "0", "1"}; + for (int i = 0; i < values.length; i++) { + Settings.Global.putInt(cr, settingName, 1); + mService.updateNetworkAvoidBadWifi(); + mService.waitForIdle(); + String msg = String.format("config=false, setting=%s", values[i]); + assertTrue(msg, mService.avoidBadWifi()); + assertFalse(msg, mService.shouldNotifyWifiUnvalidated()); + } + + mService.configRestrictsAvoidBadWifi = true; + + Settings.Global.putInt(cr, settingName, 0); + mService.updateNetworkAvoidBadWifi(); + mService.waitForIdle(); + assertFalse(mService.avoidBadWifi()); + assertFalse(mService.shouldNotifyWifiUnvalidated()); + + Settings.Global.putInt(cr, settingName, 1); + mService.updateNetworkAvoidBadWifi(); + mService.waitForIdle(); + assertTrue(mService.avoidBadWifi()); + assertFalse(mService.shouldNotifyWifiUnvalidated()); + + Settings.Global.putString(cr, settingName, null); + mService.updateNetworkAvoidBadWifi(); + mService.waitForIdle(); + assertFalse(mService.avoidBadWifi()); + assertTrue(mService.shouldNotifyWifiUnvalidated()); + } + + @SmallTest + public void testAvoidBadWifi() throws Exception { ContentResolver cr = mServiceContext.getContentResolver(); + // Pretend we're on a carrier that restricts switching away from bad wifi. + mService.configRestrictsAvoidBadWifi = true; + // File a request for cell to ensure it doesn't go down. final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); final NetworkRequest cellRequest = new NetworkRequest.Builder() @@ -2053,8 +2100,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { TestNetworkCallback validatedWifiCallback = new TestNetworkCallback(); mCm.registerNetworkCallback(validatedWifiRequest, validatedWifiCallback); - // Takes effect on every rematch. Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 0); + mService.updateNetworkAvoidBadWifi(); // Bring up validated cell. mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); @@ -2083,7 +2130,42 @@ public class ConnectivityServiceTest extends AndroidTestCase { NET_CAPABILITY_VALIDATED)); assertEquals(mCm.getActiveNetwork(), wifiNetwork); - // Simulate the user selecting "switch" on the dialog. + // Simulate switching to a carrier that does not restrict avoiding bad wifi, and expect + // that we switch back to cell. + mService.configRestrictsAvoidBadWifi = false; + mService.updateNetworkAvoidBadWifi(); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + assertEquals(mCm.getActiveNetwork(), cellNetwork); + + // Switch back to a restrictive carrier. + mService.configRestrictsAvoidBadWifi = true; + mService.updateNetworkAvoidBadWifi(); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + assertEquals(mCm.getActiveNetwork(), wifiNetwork); + + // Simulate the user selecting "switch" on the dialog, and check that we switch to cell. + mCm.setAvoidUnvalidated(wifiNetwork); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability( + NET_CAPABILITY_VALIDATED)); + assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability( + NET_CAPABILITY_VALIDATED)); + assertEquals(mCm.getActiveNetwork(), cellNetwork); + + // Disconnect and reconnect wifi to clear the one-time switch above. + mWiFiNetworkAgent.disconnect(); + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + validatedWifiCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + wifiNetwork = mWiFiNetworkAgent.getNetwork(); + + // Fail validation on wifi and expect the dialog to appear. + mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 599; + mCm.reportNetworkConnectivity(wifiNetwork, false); + validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + + // Simulate the user selecting "switch" and checking the don't ask again checkbox. Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1); mService.updateNetworkAvoidBadWifi(); @@ -2095,6 +2177,17 @@ public class ConnectivityServiceTest extends AndroidTestCase { NET_CAPABILITY_VALIDATED)); assertEquals(mCm.getActiveNetwork(), cellNetwork); + // Simulate the user turning the cellular fallback setting off and then on. + // We switch to wifi and then to cell. + Settings.Global.putString(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, null); + mService.updateNetworkAvoidBadWifi(); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + assertEquals(mCm.getActiveNetwork(), wifiNetwork); + Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1); + mService.updateNetworkAvoidBadWifi(); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + assertEquals(mCm.getActiveNetwork(), cellNetwork); + // If cell goes down, we switch to wifi. mCellNetworkAgent.disconnect(); defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 5081c25f2003..4d946dc365ad 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -661,6 +661,20 @@ public class CarrierConfigManager { public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool"; /** + * APN types that user is not allowed to modify + * @hide + */ + public static final String KEY_READ_ONLY_APN_TYPES_STRING_ARRAY = + "read_only_apn_types_string_array"; + + /** + * APN fields that user is not allowed to modify + * @hide + */ + public static final String KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY = + "read_only_apn_fields_string_array"; + + /** * Boolean indicating if intent for emergency call state changes should be broadcast * @hide */ @@ -1012,6 +1026,8 @@ public class CarrierConfigManager { sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING, ""); sDefaults.putBoolean(KEY_CSP_ENABLED_BOOL, false); sDefaults.putBoolean(KEY_ALLOW_ADDING_APNS_BOOL, true); + sDefaults.putStringArray(KEY_READ_ONLY_APN_TYPES_STRING_ARRAY, null); + sDefaults.putStringArray(KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY, null); sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false); sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false); sDefaults.putBoolean(KEY_DISABLE_SEVERE_WHEN_EXTREME_DISABLED_BOOL, true); diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java index b417a1c9e206..fdc68b9fb7c6 100644 --- a/telephony/java/com/android/internal/telephony/PhoneConstants.java +++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java @@ -140,6 +140,18 @@ public class PhoneConstants { /** APN type for Emergency PDN. This is not an IA apn, but is used * for access to carrier services in an emergency call situation. */ public static final String APN_TYPE_EMERGENCY = "emergency"; + /** Array of all APN types */ + public static final String[] APN_TYPES = {APN_TYPE_DEFAULT, + APN_TYPE_MMS, + APN_TYPE_SUPL, + APN_TYPE_DUN, + APN_TYPE_HIPRI, + APN_TYPE_FOTA, + APN_TYPE_IMS, + APN_TYPE_CBS, + APN_TYPE_IA, + APN_TYPE_EMERGENCY + }; public static final int RIL_CARD_MAX_APPS = 8; diff --git a/tests/UiBench/AndroidManifest.xml b/tests/UiBench/AndroidManifest.xml index 95bbb2139a95..3d3247cb6b5c 100644 --- a/tests/UiBench/AndroidManifest.xml +++ b/tests/UiBench/AndroidManifest.xml @@ -85,13 +85,21 @@ </activity> <activity android:name=".TrivialRecyclerViewActivity" - android:label="General/Trivial Recycler ListView" > + android:label="General/Trivial RecyclerView" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="com.android.test.uibench.TEST" /> </intent-filter> </activity> <activity + android:name=".SlowBindRecyclerViewActivity" + android:label="General/Slow Bind RecyclerView" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.uibench.TEST" /> + </intent-filter> + </activity> + <activity android:name=".ActivityTransition" android:label="Transitions/Activity Transition" > <intent-filter> diff --git a/tests/UiBench/src/com/android/test/uibench/SlowBindRecyclerViewActivity.java b/tests/UiBench/src/com/android/test/uibench/SlowBindRecyclerViewActivity.java new file mode 100644 index 000000000000..e32862f7b7fd --- /dev/null +++ b/tests/UiBench/src/com/android/test/uibench/SlowBindRecyclerViewActivity.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test.uibench; + +import android.content.Context; +import android.os.Trace; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import com.android.test.uibench.recyclerview.RvBoxAdapter; +import com.android.test.uibench.recyclerview.RvCompatListActivity; + +import java.util.concurrent.TimeUnit; + +public class SlowBindRecyclerViewActivity extends RvCompatListActivity { + /** + * Spin wait. Used instead of sleeping so a core is used up for the duration, and so + * traces/sampled profiling show the sections as expensive, and not just a scheduling mistake. + */ + private static void spinWaitMs(long ms) { + long start = System.nanoTime(); + while (System.nanoTime() - start < TimeUnit.MILLISECONDS.toNanos(ms)); + } + + @Override + protected RecyclerView.LayoutManager createLayoutManager(Context context) { + return new GridLayoutManager(context, 3); + } + + @Override + protected RecyclerView.Adapter createAdapter() { + return new RvBoxAdapter(this, TextUtils.buildSimpleStringList()) { + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + Trace.beginSection("bind item " + position); + + spinWaitMs(3); + super.onBindViewHolder(holder, position); + Trace.endSection(); + } + }; + } +} diff --git a/tests/UiBench/src/com/android/test/uibench/recyclerview/RvBoxAdapter.java b/tests/UiBench/src/com/android/test/uibench/recyclerview/RvBoxAdapter.java new file mode 100644 index 000000000000..3440f192e7c5 --- /dev/null +++ b/tests/UiBench/src/com/android/test/uibench/recyclerview/RvBoxAdapter.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test.uibench.recyclerview; + +import android.content.Context; +import android.graphics.Color; +import android.support.v7.widget.RecyclerView; +import android.util.TypedValue; +import android.view.ViewGroup; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class RvBoxAdapter extends RecyclerView.Adapter<RvBoxAdapter.ViewHolder> { + + private int mBackground; + + private List<String> mValues; + + public static class ViewHolder extends RecyclerView.ViewHolder { + public TextView mTextView; + + public ViewHolder(TextView v) { + super(v); + mTextView = v; + } + + @Override + public String toString() { + return super.toString() + " '" + mTextView.getText(); + } + } + + public RvBoxAdapter(Context context, String[] strings) { + TypedValue val = new TypedValue(); + if (context.getTheme() != null) { + context.getTheme().resolveAttribute( + android.R.attr.selectableItemBackground, val, true); + } + mBackground = val.resourceId; + mValues = new ArrayList<>(); + Collections.addAll(mValues, strings); + } + + @Override + public RvBoxAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + final ViewHolder h = new ViewHolder(new TextView(parent.getContext())); + h.mTextView.setMinimumHeight(128); + h.mTextView.setPadding(20, 0, 20, 0); + h.mTextView.setFocusable(true); + h.mTextView.setBackgroundResource(mBackground); + RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + lp.leftMargin = 10; + lp.rightMargin = 5; + lp.topMargin = 20; + lp.bottomMargin = 15; + h.mTextView.setLayoutParams(lp); + return h; + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + holder.mTextView.setText(position + ":" + mValues.get(position)); + holder.mTextView.setMinHeight((200 + mValues.get(position).length() * 10)); + holder.mTextView.setBackgroundColor(getBackgroundColor(position)); + } + + private int getBackgroundColor(int position) { + switch (position % 4) { + case 0: return Color.LTGRAY; + case 1: return Color.RED; + case 2: return Color.DKGRAY; + case 3: return Color.BLUE; + } + return Color.TRANSPARENT; + } + + @Override + public int getItemCount() { + return mValues.size(); + } +} diff --git a/tests/UiBench/src/com/android/test/uibench/recyclerview/RvCompatListActivity.java b/tests/UiBench/src/com/android/test/uibench/recyclerview/RvCompatListActivity.java index e08dbc66e7f2..939b66198d72 100644 --- a/tests/UiBench/src/com/android/test/uibench/recyclerview/RvCompatListActivity.java +++ b/tests/UiBench/src/com/android/test/uibench/recyclerview/RvCompatListActivity.java @@ -26,7 +26,6 @@ import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; import com.android.test.uibench.R; diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index 76522f95fbd4..0c3231bcde60 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -460,6 +460,16 @@ public class IWindowManagerImpl implements IWindowManager { } @Override + public void setRecentsVisibility(boolean visible) { + // TODO Auto-generated method stub + } + + @Override + public void setTvPipVisibility(boolean visible) { + // TODO Auto-generated method stub + } + + @Override public void stopAppFreezingScreen(IBinder arg0, boolean arg1) throws RemoteException { // TODO Auto-generated method stub } |