summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk25
-rw-r--r--core/java/android/app/DownloadManager.java6
-rw-r--r--core/java/android/app/Notification.java2
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java4
-rw-r--r--core/java/android/hardware/camera2/DngCreator.java27
-rw-r--r--core/java/android/net/ConnectivityManager.java21
-rw-r--r--core/java/android/net/IConnectivityManager.aidl1
-rw-r--r--core/java/android/os/PowerManager.java29
-rw-r--r--core/java/android/os/RecoverySystem.java6
-rwxr-xr-xcore/java/android/provider/Settings.java22
-rw-r--r--core/java/android/service/dreams/DreamService.java160
-rw-r--r--core/java/android/view/DragEvent.java2
-rw-r--r--core/java/android/view/IWindowManager.aidl10
-rw-r--r--core/java/android/view/Surface.java12
-rw-r--r--core/java/android/view/View.java68
-rw-r--r--core/java/android/view/ViewGroup.java161
-rw-r--r--core/java/android/view/ViewRootImpl.java44
-rw-r--r--core/java/android/view/WindowManagerPolicy.java10
-rw-r--r--docs/html/_redirects.yaml3
-rw-r--r--docs/html/develop/index.jd18
-rw-r--r--docs/html/google/play/billing/billing_admin.jd44
-rwxr-xr-xdocs/html/guide/topics/location/strategies.jd21
-rwxr-xr-xdocs/html/guide/topics/manifest/uses-feature-element.jd14
-rw-r--r--docs/html/guide/topics/media/camera.jd2
-rw-r--r--docs/html/guide/topics/resources/multilingual-support.jd18
-rw-r--r--docs/html/images/develop/hero-layout-editor_2x.pngbin0 -> 7018 bytes
-rw-r--r--docs/html/index.jd34
-rw-r--r--docs/html/jd_extras_en.js6
-rw-r--r--docs/html/topic/arc/index.jd2
-rw-r--r--docs/html/training/_book.yaml7
-rw-r--r--docs/html/training/constraint-layout/images/alignment-constraint-offset_2x.pngbin0 -> 8218 bytes
-rw-r--r--docs/html/training/constraint-layout/images/alignment-constraint_2x.pngbin0 -> 7172 bytes
-rw-r--r--docs/html/training/constraint-layout/images/baseline-constraint_2x.pngbin0 -> 5136 bytes
-rw-r--r--docs/html/training/constraint-layout/images/constraint-fail-fixed_2x.pngbin0 -> 9688 bytes
-rw-r--r--docs/html/training/constraint-layout/images/constraint-fail_2x.pngbin0 -> 9419 bytes
-rw-r--r--docs/html/training/constraint-layout/images/layout-editor-convert-to-constraint_2x.pngbin0 -> 21264 bytes
-rw-r--r--docs/html/training/constraint-layout/images/layout-editor-margin-callout_2-2_2x.pngbin0 -> 8136 bytes
-rw-r--r--docs/html/training/constraint-layout/images/layout-editor-properties-callouts_2-2_2x.pngbin0 -> 40984 bytes
-rw-r--r--docs/html/training/constraint-layout/images/layout-editor_2-2_2x.pngbin0 -> 206243 bytes
-rw-r--r--docs/html/training/constraint-layout/images/parent-constraint_2x.pngbin0 -> 5613 bytes
-rw-r--r--docs/html/training/constraint-layout/images/position-constraint_2x.pngbin0 -> 7161 bytes
-rw-r--r--docs/html/training/constraint-layout/images/thumbnail-add-layout-guideline_2-2.pngbin0 -> 78602 bytes
-rw-r--r--docs/html/training/constraint-layout/images/thumbnail-adjust-constraint-bias.pngbin0 -> 308699 bytes
-rw-r--r--docs/html/training/constraint-layout/images/thumbnail-studio-constraint-first.pngbin0 -> 61879 bytes
-rw-r--r--docs/html/training/constraint-layout/images/thumbnail-studio-constraint-second.pngbin0 -> 65594 bytes
-rw-r--r--docs/html/training/constraint-layout/index.html498
-rw-r--r--docs/html/training/material/images/palette-library-color-profiles_2-1_2x.pngbin0 -> 295117 bytes
-rw-r--r--docs/html/training/material/images/palette-library-title-text-color_2-1_2x.pngbin0 -> 269351 bytes
-rw-r--r--docs/html/training/material/index.jd4
-rw-r--r--docs/html/training/material/palette-colors.html310
-rw-r--r--docs/html/training/testing/unit-testing/instrumented-unit-tests.jd5
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java108
-rw-r--r--media/java/android/media/ImageReader.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java44
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_data_disabled.xml5
-rw-r--r--packages/SystemUI/res/drawable/stat_sys_data_disabled.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java7
-rw-r--r--packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java5
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java14
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java100
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java4
-rw-r--r--services/core/java/com/android/server/ServiceWatcher.java17
-rw-r--r--services/core/java/com/android/server/TextServicesManagerService.java106
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java14
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java21
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java16
-rw-r--r--services/core/java/com/android/server/connectivity/PacManager.java36
-rw-r--r--services/core/java/com/android/server/dreams/DreamController.java33
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java10
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java7
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java58
-rw-r--r--services/core/java/com/android/server/pm/OtaDexoptService.java1
-rw-r--r--services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java13
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java25
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java29
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java26
-rw-r--r--services/net/java/android/net/ip/IpManager.java50
-rw-r--r--services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java97
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java16
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneConstants.java12
-rw-r--r--tests/UiBench/AndroidManifest.xml10
-rw-r--r--tests/UiBench/src/com/android/test/uibench/SlowBindRecyclerViewActivity.java55
-rw-r--r--tests/UiBench/src/com/android/test/uibench/recyclerview/RvBoxAdapter.java99
-rw-r--r--tests/UiBench/src/com/android/test/uibench/recyclerview/RvCompatListActivity.java1
-rw-r--r--tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java10
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>
&nbsp;&nbsp;&nbsp;&nbsp;<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>&lt;uses-feature android:name="android.hardware.camera" android:required="false" /&gt;</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
new file mode 100644
index 000000000000..56dfbf38719b
--- /dev/null
+++ b/docs/html/images/develop/hero-layout-editor_2x.png
Binary files differ
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>
+ &nbsp;&nbsp;&nbsp;&nbsp;<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.
&lt;!-- Some Chromebooks don't support touch. Although not essential,
it's a good idea to explicitly include this declaration. --&gt;
&lt;uses-feature android:name="android.hardware.touchscreen"
- required="false" /&gt;
+ android:required="false" /&gt;
&lt;/manifest&gt;
</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
new file mode 100644
index 000000000000..1e4867e6513a
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/alignment-constraint-offset_2x.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/alignment-constraint_2x.png b/docs/html/training/constraint-layout/images/alignment-constraint_2x.png
new file mode 100644
index 000000000000..afe7d4aed282
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/alignment-constraint_2x.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/baseline-constraint_2x.png b/docs/html/training/constraint-layout/images/baseline-constraint_2x.png
new file mode 100644
index 000000000000..dfc35226fdb5
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/baseline-constraint_2x.png
Binary files differ
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
new file mode 100644
index 000000000000..be9d54f4fbf5
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/constraint-fail-fixed_2x.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/constraint-fail_2x.png b/docs/html/training/constraint-layout/images/constraint-fail_2x.png
new file mode 100644
index 000000000000..3f28ef7906ab
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/constraint-fail_2x.png
Binary files differ
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
new file mode 100644
index 000000000000..ace31a6105d9
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/layout-editor-convert-to-constraint_2x.png
Binary files differ
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
new file mode 100644
index 000000000000..07680227fbb7
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/layout-editor-margin-callout_2-2_2x.png
Binary files differ
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
new file mode 100644
index 000000000000..b4ffb2cd946a
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/layout-editor-properties-callouts_2-2_2x.png
Binary files differ
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
new file mode 100644
index 000000000000..72a4e401a05c
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/layout-editor_2-2_2x.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/parent-constraint_2x.png b/docs/html/training/constraint-layout/images/parent-constraint_2x.png
new file mode 100644
index 000000000000..0414f1d5b34b
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/parent-constraint_2x.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/position-constraint_2x.png b/docs/html/training/constraint-layout/images/position-constraint_2x.png
new file mode 100644
index 000000000000..9f93e72dcd4d
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/position-constraint_2x.png
Binary files differ
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
new file mode 100644
index 000000000000..f863e5f99bd1
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/thumbnail-add-layout-guideline_2-2.png
Binary files differ
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
new file mode 100644
index 000000000000..d61e9b2354f1
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/thumbnail-adjust-constraint-bias.png
Binary files differ
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
new file mode 100644
index 000000000000..97471025b007
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/thumbnail-studio-constraint-first.png
Binary files differ
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
new file mode 100644
index 000000000000..940b84955278
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/thumbnail-studio-constraint-second.png
Binary files differ
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
new file mode 100644
index 000000000000..d14ec32c7500
--- /dev/null
+++ b/docs/html/training/material/images/palette-library-color-profiles_2-1_2x.png
Binary files differ
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
new file mode 100644
index 000000000000..883adba6a430
--- /dev/null
+++ b/docs/html/training/material/images/palette-library-title-text-color_2-1_2x.png
Binary files differ
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&lt;<em>Profile</em>&gt;Color()</code>
+methods returns the color in the palette associated with that particular profile,
+where <code>&lt;<em>Profile</em>&gt;</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&lt;<em>Profile</em>&gt;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&lt;<em>Profile</em>&gt;Swatch()</code>
+methods returns the swatch associated with that particular profile,
+where <code>&lt;<em>Profile</em>&gt;</code> is replaced by the name of one of the six
+color profiles. Although the palette’s <code>get&lt;<em>Profile</em>&gt;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>
* &lt;vector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
* android:height=&quot;64dp&quot;
@@ -96,17 +113,20 @@ import java.util.ArrayList;
* &lt;/group&gt;
* &lt;/vector&gt;
* </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>
* &lt;animated-vector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
- * android:drawable=&quot;@drawable/vectordrawable&quot; &gt;
+ * android:drawable=&quot;@drawable/vectordrawable&quot; &gt;
* &lt;target
* android:name=&quot;rotationGroup&quot;
* android:animation=&quot;@anim/rotation&quot; /&gt;
@@ -114,39 +134,43 @@ import java.util.ArrayList;
* android:name=&quot;v&quot;
* android:animation=&quot;@anim/path_morph&quot; /&gt;
* &lt;/animated-vector&gt;
- * </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>
* &lt;objectAnimator
* android:duration=&quot;6000&quot;
* android:propertyName=&quot;rotation&quot;
* android:valueFrom=&quot;0&quot;
* android:valueTo=&quot;360&quot; /&gt;
- * </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>
* &lt;set xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;
* &lt;objectAnimator
* android:duration=&quot;3000&quot;
* android:propertyName=&quot;pathData&quot;
- * android:valueFrom=&quot;M300,70 l 0,-70 70,70 0,0 -70,70z&quot;
+ * android:valueFrom=&quot;M300,70 l 0,-70 70,70 0,0 -70,70z&quot;
* android:valueTo=&quot;M300,70 l 0,-70 70,0 0,140 -70,0 z&quot;
* android:valueType=&quot;pathType&quot;/&gt;
* &lt;/set&gt;
- * </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>
* &lt;animated-vector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot; &gt;
@@ -185,7 +209,7 @@ import java.util.ArrayList;
* &lt;objectAnimator
* android:duration=&quot;3000&quot;
* android:propertyName=&quot;pathData&quot;
- * android:valueFrom=&quot;M300,70 l 0,-70 70,70 0,0 -70,70z&quot;
+ * android:valueFrom=&quot;M300,70 l 0,-70 70,70 0,0 -70,70z&quot;
* android:valueTo=&quot;M300,70 l 0,-70 70,0 0,140 -70,0 z&quot;
* android:valueType=&quot;pathType&quot;/&gt;
* &lt;/set&gt;
@@ -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
}