diff options
68 files changed, 1276 insertions, 314 deletions
diff --git a/Android.mk b/Android.mk index 320fe2f2ed17..0b33301ce5ac 100644 --- a/Android.mk +++ b/Android.mk @@ -496,6 +496,10 @@ LOCAL_JACK_FLAGS := --multi-dex native LOCAL_RMTYPEDEFS := true +ifeq ($(EMMA_INSTRUMENT_FRAMEWORK),true) +LOCAL_EMMA_INSTRUMENT := true +endif + include $(BUILD_JAVA_LIBRARY) framework_module := $(LOCAL_INSTALLED_MODULE) diff --git a/api/current.txt b/api/current.txt index c6316271c636..08c9fd6eac1a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -29362,6 +29362,7 @@ package android.os { method public android.os.Bundle getUserRestrictions(); method public android.os.Bundle getUserRestrictions(android.os.UserHandle); method public boolean hasUserRestriction(java.lang.String); + method public boolean isDemoUser(); method public boolean isQuietModeEnabled(android.os.UserHandle); method public boolean isSystemUser(); method public boolean isUserAGoat(); diff --git a/api/system-current.txt b/api/system-current.txt index 3fa518a43e49..7aa5394a9021 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -31887,6 +31887,7 @@ package android.os { method public android.os.Bundle getUserRestrictions(); method public android.os.Bundle getUserRestrictions(android.os.UserHandle); method public boolean hasUserRestriction(java.lang.String); + method public boolean isDemoUser(); method public boolean isManagedProfile(); method public boolean isManagedProfile(int); method public boolean isQuietModeEnabled(android.os.UserHandle); diff --git a/api/test-current.txt b/api/test-current.txt index 993f388f15ab..003e50b96533 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -29433,6 +29433,7 @@ package android.os { method public android.os.Bundle getUserRestrictions(); method public android.os.Bundle getUserRestrictions(android.os.UserHandle); method public boolean hasUserRestriction(java.lang.String); + method public boolean isDemoUser(); method public boolean isQuietModeEnabled(android.os.UserHandle); method public boolean isSystemUser(); method public boolean isUserAGoat(); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 05f49c5a1f02..4104d72ec5ad 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -762,14 +762,13 @@ public class Notification implements Parcelable public Bundle extras = new Bundle(); /** - * All pending intents in the notification extras (notification extras, actions extras, - * and remote input extras) as the system needs to be able to access them but touching - * the extras bundle in the system process is not safe because the bundle may contain + * All pending intents in the notification as the system needs to be able to access them but + * touching the extras bundle in the system process is not safe because the bundle may contain * custom parcelable objects. * * @hide */ - public ArraySet<PendingIntent> extrasPendingIntents; + public ArraySet<PendingIntent> allPendingIntents; /** * {@link #extras} key: this is the title of the notification, @@ -1593,7 +1592,7 @@ public class Notification implements Parcelable // intents in extras are always written as the last entry. readFromParcelImpl(parcel); // Must be read last! - extrasPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null); + allPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null); } private void readFromParcelImpl(Parcel parcel) @@ -1751,8 +1750,8 @@ public class Notification implements Parcelable } } - if (!ArrayUtils.isEmpty(extrasPendingIntents)) { - that.extrasPendingIntents = new ArraySet<>(extrasPendingIntents); + if (!ArrayUtils.isEmpty(allPendingIntents)) { + that.allPendingIntents = new ArraySet<>(allPendingIntents); } if (this.actions != null) { @@ -1878,15 +1877,15 @@ public class Notification implements Parcelable // cannot look into the extras as there may be parcelables there that // the platform does not know how to handle. To go around that we have // an explicit list of the pending intents in the extras bundle. - final boolean collectPendingIntents = (extrasPendingIntents == null); + final boolean collectPendingIntents = (allPendingIntents == null); if (collectPendingIntents) { PendingIntent.setOnMarshaledListener( (PendingIntent intent, Parcel out, int outFlags) -> { if (parcel == out) { - if (extrasPendingIntents == null) { - extrasPendingIntents = new ArraySet<>(); + if (allPendingIntents == null) { + allPendingIntents = new ArraySet<>(); } - extrasPendingIntents.add(intent); + allPendingIntents.add(intent); } }); } @@ -1895,7 +1894,7 @@ public class Notification implements Parcelable // want to intercept all pending events written to the pacel. writeToParcelImpl(parcel, flags); // Must be written last! - parcel.writeArraySet(extrasPendingIntents); + parcel.writeArraySet(allPendingIntents); } finally { if (collectPendingIntents) { PendingIntent.setOnMarshaledListener(null); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index b06568ca0b2d..fbe16c5db717 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -132,6 +132,9 @@ public abstract class PackageManager { MATCH_SYSTEM_ONLY, MATCH_FACTORY_ONLY, MATCH_DEBUG_TRIAGED_MISSING, + GET_DISABLED_COMPONENTS, + GET_DISABLED_UNTIL_USED_COMPONENTS, + GET_UNINSTALLED_PACKAGES, }) @Retention(RetentionPolicy.SOURCE) public @interface PackageInfoFlags {} @@ -143,6 +146,7 @@ public abstract class PackageManager { MATCH_UNINSTALLED_PACKAGES, MATCH_SYSTEM_ONLY, MATCH_DEBUG_TRIAGED_MISSING, + GET_UNINSTALLED_PACKAGES, }) @Retention(RetentionPolicy.SOURCE) public @interface ApplicationInfoFlags {} @@ -160,6 +164,9 @@ public abstract class PackageManager { MATCH_DIRECT_BOOT_UNAWARE, MATCH_SYSTEM_ONLY, MATCH_UNINSTALLED_PACKAGES, + GET_DISABLED_COMPONENTS, + GET_DISABLED_UNTIL_USED_COMPONENTS, + GET_UNINSTALLED_PACKAGES, }) @Retention(RetentionPolicy.SOURCE) public @interface ComponentInfoFlags {} @@ -178,6 +185,9 @@ public abstract class PackageManager { MATCH_DIRECT_BOOT_UNAWARE, MATCH_SYSTEM_ONLY, MATCH_UNINSTALLED_PACKAGES, + GET_DISABLED_COMPONENTS, + GET_DISABLED_UNTIL_USED_COMPONENTS, + GET_UNINSTALLED_PACKAGES, }) @Retention(RetentionPolicy.SOURCE) public @interface ResolveInfoFlags {} diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index f59a7dda5696..82cd448e7776 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1876,7 +1876,7 @@ public class PackageParser { sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestUsesSdk); - int minVers = 0; + int minVers = 1; String minCode = null; int targetVers = 0; String targetCode = null; @@ -1903,9 +1903,6 @@ public class PackageParser { } else { // If it's not a string, it's an integer. targetVers = val.data; - if (minVers == 0) { - minVers = targetVers; - } } } diff --git a/core/java/android/net/ConnectivityMetricsLogger.java b/core/java/android/net/ConnectivityMetricsLogger.java index 73734958f512..029c5bdccd41 100644 --- a/core/java/android/net/ConnectivityMetricsLogger.java +++ b/core/java/android/net/ConnectivityMetricsLogger.java @@ -23,6 +23,8 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + /** {@hide} */ @SystemApi public class ConnectivityMetricsLogger { @@ -49,8 +51,14 @@ public class ConnectivityMetricsLogger { private int mNumSkippedEvents; public ConnectivityMetricsLogger() { - mService = IConnectivityMetricsLogger.Stub.asInterface(ServiceManager.getService( - CONNECTIVITY_METRICS_LOGGER_SERVICE)); + this(IConnectivityMetricsLogger.Stub.asInterface( + ServiceManager.getService(CONNECTIVITY_METRICS_LOGGER_SERVICE))); + } + + /** {@hide} */ + @VisibleForTesting + public ConnectivityMetricsLogger(IConnectivityMetricsLogger service) { + mService = service; } /** @@ -153,11 +161,10 @@ public class ConnectivityMetricsLogger { public boolean unregister(PendingIntent newEventsIntent) { try { mService.unregister(newEventsIntent); + return true; } catch (RemoteException e) { Log.e(TAG, "IConnectivityMetricsLogger.unregister", e); return false; } - - return true; } } diff --git a/core/java/android/net/metrics/DefaultNetworkEvent.java b/core/java/android/net/metrics/DefaultNetworkEvent.java index f8b59925cb91..b881fbb1dacc 100644 --- a/core/java/android/net/metrics/DefaultNetworkEvent.java +++ b/core/java/android/net/metrics/DefaultNetworkEvent.java @@ -25,7 +25,7 @@ import android.os.Parcelable; * {@hide} */ @SystemApi -public final class DefaultNetworkEvent extends IpConnectivityEvent implements Parcelable { +public final class DefaultNetworkEvent implements Parcelable { // The ID of the network that has become the new default or NETID_UNSET if none. public final int netId; // The list of transport types of the new default network, for example TRANSPORT_WIFI, as @@ -37,7 +37,8 @@ public final class DefaultNetworkEvent extends IpConnectivityEvent implements Pa public final boolean prevIPv4; public final boolean prevIPv6; - private DefaultNetworkEvent(int netId, int[] transportTypes, + /** {@hide} */ + public DefaultNetworkEvent(int netId, int[] transportTypes, int prevNetId, boolean prevIPv4, boolean prevIPv6) { this.netId = netId; this.transportTypes = transportTypes; @@ -105,6 +106,5 @@ public final class DefaultNetworkEvent extends IpConnectivityEvent implements Pa public static void logEvent( int netId, int[] transports, int prevNetId, boolean hadIPv4, boolean hadIPv6) { - logEvent(new DefaultNetworkEvent(netId, transports, prevNetId, hadIPv4, hadIPv6)); } -}; +} diff --git a/core/java/android/net/metrics/DhcpClientEvent.java b/core/java/android/net/metrics/DhcpClientEvent.java index ec560bf64ccc..3fe68b40e9f4 100644 --- a/core/java/android/net/metrics/DhcpClientEvent.java +++ b/core/java/android/net/metrics/DhcpClientEvent.java @@ -24,11 +24,12 @@ import android.os.Parcelable; * {@hide} */ @SystemApi -public final class DhcpClientEvent extends IpConnectivityEvent implements Parcelable { +public final class DhcpClientEvent implements Parcelable { public final String ifName; public final String msg; - private DhcpClientEvent(String ifName, String msg) { + /** {@hide} */ + public DhcpClientEvent(String ifName, String msg) { this.ifName = ifName; this.msg = msg; } @@ -64,6 +65,5 @@ public final class DhcpClientEvent extends IpConnectivityEvent implements Parcel }; public static void logStateEvent(String ifName, String state) { - logEvent(new DhcpClientEvent(ifName, state)); } -}; +} diff --git a/core/java/android/net/metrics/DhcpErrorEvent.java b/core/java/android/net/metrics/DhcpErrorEvent.java index 84795b89bb6b..4206886c6bd0 100644 --- a/core/java/android/net/metrics/DhcpErrorEvent.java +++ b/core/java/android/net/metrics/DhcpErrorEvent.java @@ -27,7 +27,7 @@ import com.android.internal.util.MessageUtils; * {@hide} Event class used to record error events when parsing DHCP response packets. */ @SystemApi -public final class DhcpErrorEvent extends IpConnectivityEvent implements Parcelable { +public final class DhcpErrorEvent implements Parcelable { public static final int L2_ERROR = 1; public static final int L3_ERROR = 2; public static final int L4_ERROR = 3; @@ -61,7 +61,8 @@ public final class DhcpErrorEvent extends IpConnectivityEvent implements Parcela // byte 3: optional code public final int errorCode; - private DhcpErrorEvent(String ifName, int errorCode) { + /** {@hide} */ + public DhcpErrorEvent(String ifName, int errorCode) { this.ifName = ifName; this.errorCode = errorCode; } @@ -92,11 +93,9 @@ public final class DhcpErrorEvent extends IpConnectivityEvent implements Parcela }; public static void logParseError(String ifName, int errorCode) { - logEvent(new DhcpErrorEvent(ifName, errorCode)); } public static void logReceiveError(String ifName) { - logEvent(new DhcpErrorEvent(ifName, RECEIVE_ERROR)); } public static int errorCodeWithOption(int errorCode, int option) { diff --git a/core/java/android/net/metrics/DnsEvent.java b/core/java/android/net/metrics/DnsEvent.java index b94dda079cd8..9eb8bdb579d0 100644 --- a/core/java/android/net/metrics/DnsEvent.java +++ b/core/java/android/net/metrics/DnsEvent.java @@ -24,7 +24,7 @@ import android.os.Parcelable; * {@hide} */ @SystemApi -final public class DnsEvent extends IpConnectivityEvent implements Parcelable { +final public class DnsEvent implements Parcelable { public final int netId; // The event type is currently only 1 or 2, so we store it as a byte. @@ -37,7 +37,8 @@ final public class DnsEvent extends IpConnectivityEvent implements Parcelable { // queries. public final int[] latenciesMs; - private DnsEvent(int netId, byte[] eventTypes, byte[] returnCodes, int[] latenciesMs) { + /** {@hide} */ + public DnsEvent(int netId, byte[] eventTypes, byte[] returnCodes, int[] latenciesMs) { this.netId = netId; this.eventTypes = eventTypes; this.returnCodes = returnCodes; @@ -82,6 +83,5 @@ final public class DnsEvent extends IpConnectivityEvent implements Parcelable { public static void logEvent( int netId, byte[] eventTypes, byte[] returnCodes, int[] latenciesMs) { - logEvent(new DnsEvent(netId, eventTypes, returnCodes, latenciesMs)); } } diff --git a/core/java/android/net/metrics/IpConnectivityEvent.java b/core/java/android/net/metrics/IpConnectivityEvent.java deleted file mode 100644 index d3771dcb60af..000000000000 --- a/core/java/android/net/metrics/IpConnectivityEvent.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 android.net.metrics; - -import android.os.Parcelable; - -/** {@hide} */ -public abstract class IpConnectivityEvent { - private static final IpConnectivityLog sMetricsLog = new IpConnectivityLog(); - - static <T extends IpConnectivityEvent & Parcelable> void logEvent(T event) { - sMetricsLog.log(System.currentTimeMillis(), event); - } -} diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java index 233ff74a145b..a7c1d40e90de 100644 --- a/core/java/android/net/metrics/IpConnectivityLog.java +++ b/core/java/android/net/metrics/IpConnectivityLog.java @@ -18,28 +18,40 @@ package android.net.metrics; import android.net.ConnectivityMetricsEvent; import android.net.ConnectivityMetricsLogger; +import android.net.IConnectivityMetricsLogger; import android.os.Parcelable; import android.os.RemoteException; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + /** * Specialization of the ConnectivityMetricsLogger class for recording IP connectivity events. * {@hide} */ -class IpConnectivityLog extends ConnectivityMetricsLogger { +public class IpConnectivityLog extends ConnectivityMetricsLogger { private static String TAG = "IpConnectivityMetricsLogger"; private static final boolean DBG = false; + public IpConnectivityLog() { + // mService initialized in super constructor. + } + + @VisibleForTesting + public IpConnectivityLog(IConnectivityMetricsLogger service) { + super(service); + } + /** * Log an IpConnectivity event. Contrary to logEvent(), this method does not * keep track of skipped events and is thread-safe for callers. * * @param timestamp is the epoch timestamp of the event in ms. - * @param data is a Parcelable IpConnectivityEvent instance representing the event. + * @param data is a Parcelable instance representing the event. * * @return true if the event was successfully logged. */ - public <T extends IpConnectivityEvent & Parcelable> boolean log(long timestamp, T data) { + public boolean log(long timestamp, Parcelable data) { if (mService == null) { if (DBG) { Log.d(TAG, CONNECTIVITY_METRICS_LOGGER_SERVICE + " service not ready"); @@ -67,4 +79,8 @@ class IpConnectivityLog extends ConnectivityMetricsLogger { return false; } } + + public void log(Parcelable event) { + log(System.currentTimeMillis(), event); + } } diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java index 0940bd06c3d3..a39061748ac3 100644 --- a/core/java/android/net/metrics/IpManagerEvent.java +++ b/core/java/android/net/metrics/IpManagerEvent.java @@ -27,7 +27,7 @@ import com.android.internal.util.MessageUtils; * {@hide} */ @SystemApi -public final class IpManagerEvent extends IpConnectivityEvent implements Parcelable { +public final class IpManagerEvent implements Parcelable { public static final int PROVISIONING_OK = 1; public static final int PROVISIONING_FAIL = 2; @@ -37,7 +37,8 @@ public final class IpManagerEvent extends IpConnectivityEvent implements Parcela public final int eventType; public final long durationMs; - private IpManagerEvent(String ifName, int eventType, long duration) { + /** {@hide} */ + public IpManagerEvent(String ifName, int eventType, long duration) { this.ifName = ifName; this.eventType = eventType; this.durationMs = duration; @@ -71,7 +72,6 @@ public final class IpManagerEvent extends IpConnectivityEvent implements Parcela }; public static void logEvent(int eventType, String ifName, long durationMs) { - logEvent(new IpManagerEvent(ifName, eventType, durationMs)); } @Override @@ -84,4 +84,4 @@ public final class IpManagerEvent extends IpConnectivityEvent implements Parcela static final SparseArray<String> constants = MessageUtils.findMessageNames( new Class[]{IpManagerEvent.class}, new String[]{"PROVISIONING_", "COMPLETE_"}); } -}; +} diff --git a/core/java/android/net/metrics/IpReachabilityEvent.java b/core/java/android/net/metrics/IpReachabilityEvent.java index d40389c03986..7d0229191563 100644 --- a/core/java/android/net/metrics/IpReachabilityEvent.java +++ b/core/java/android/net/metrics/IpReachabilityEvent.java @@ -27,7 +27,7 @@ import com.android.internal.util.MessageUtils; * {@hide} */ @SystemApi -public final class IpReachabilityEvent extends IpConnectivityEvent implements Parcelable { +public final class IpReachabilityEvent implements Parcelable { public static final int PROBE = 1 << 8; public static final int NUD_FAILED = 2 << 8; @@ -41,7 +41,8 @@ public final class IpReachabilityEvent extends IpConnectivityEvent implements Pa // byte 3: kernel errno from RTNetlink or IpReachabilityMonitor public final int eventType; - private IpReachabilityEvent(String ifName, int eventType) { + /** {@hide} */ + public IpReachabilityEvent(String ifName, int eventType) { this.ifName = ifName; this.eventType = eventType; } @@ -72,15 +73,12 @@ public final class IpReachabilityEvent extends IpConnectivityEvent implements Pa }; public static void logProbeEvent(String ifName, int nlErrorCode) { - logEvent(new IpReachabilityEvent(ifName, PROBE | (nlErrorCode & 0xFF))); } public static void logNudFailed(String ifName) { - logEvent(new IpReachabilityEvent(ifName, NUD_FAILED)); } public static void logProvisioningLost(String ifName) { - logEvent(new IpReachabilityEvent(ifName, PROVISIONING_LOST)); } @Override @@ -94,4 +92,4 @@ public final class IpReachabilityEvent extends IpConnectivityEvent implements Pa MessageUtils.findMessageNames(new Class[]{IpReachabilityEvent.class}, new String[]{"PROBE", "PROVISIONING_", "NUD_"}); } -}; +} diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java index 08c9c758bbc8..cdfe386d430c 100644 --- a/core/java/android/net/metrics/NetworkEvent.java +++ b/core/java/android/net/metrics/NetworkEvent.java @@ -27,7 +27,7 @@ import com.android.internal.util.MessageUtils; * {@hide} */ @SystemApi -public final class NetworkEvent extends IpConnectivityEvent implements Parcelable { +public final class NetworkEvent implements Parcelable { public static final int NETWORK_CONNECTED = 1; public static final int NETWORK_VALIDATED = 2; @@ -41,12 +41,18 @@ public final class NetworkEvent extends IpConnectivityEvent implements Parcelabl public final int eventType; public final long durationMs; - private NetworkEvent(int netId, int eventType, long durationMs) { + /** {@hide} */ + public NetworkEvent(int netId, int eventType, long durationMs) { this.netId = netId; this.eventType = eventType; this.durationMs = durationMs; } + /** {@hide} */ + public NetworkEvent(int netId, int eventType) { + this(netId, eventType, 0); + } + private NetworkEvent(Parcel in) { netId = in.readInt(); eventType = in.readInt(); @@ -75,15 +81,12 @@ public final class NetworkEvent extends IpConnectivityEvent implements Parcelabl }; public static void logEvent(int netId, int eventType) { - logEvent(new NetworkEvent(netId, eventType, 0)); } public static void logValidated(int netId, long durationMs) { - logEvent(new NetworkEvent(netId, NETWORK_VALIDATED, durationMs)); } public static void logCaptivePortalFound(int netId, long durationMs) { - logEvent(new NetworkEvent(netId, NETWORK_CAPTIVE_PORTAL_FOUND, durationMs)); } @Override @@ -96,4 +99,4 @@ public final class NetworkEvent extends IpConnectivityEvent implements Parcelabl static final SparseArray<String> constants = MessageUtils.findMessageNames( new Class[]{NetworkEvent.class}, new String[]{"NETWORK_"}); } -}; +} diff --git a/core/java/android/net/metrics/ValidationProbeEvent.java b/core/java/android/net/metrics/ValidationProbeEvent.java index 751c35f8a144..d5ad0f6c25a9 100644 --- a/core/java/android/net/metrics/ValidationProbeEvent.java +++ b/core/java/android/net/metrics/ValidationProbeEvent.java @@ -27,7 +27,7 @@ import com.android.internal.util.MessageUtils; * {@hide} */ @SystemApi -public final class ValidationProbeEvent extends IpConnectivityEvent implements Parcelable { +public final class ValidationProbeEvent implements Parcelable { public static final int PROBE_DNS = 0; public static final int PROBE_HTTP = 1; @@ -42,7 +42,8 @@ public final class ValidationProbeEvent extends IpConnectivityEvent implements P public final int probeType; public final int returnCode; - private ValidationProbeEvent(int netId, long durationMs, int probeType, int returnCode) { + /** @hide */ + public ValidationProbeEvent(int netId, long durationMs, int probeType, int returnCode) { this.netId = netId; this.durationMs = durationMs; this.probeType = probeType; @@ -84,7 +85,6 @@ public final class ValidationProbeEvent extends IpConnectivityEvent implements P } public static void logEvent(int netId, long durationMs, int probeType, int returnCode) { - logEvent(new ValidationProbeEvent(netId, durationMs, probeType, returnCode)); } @Override @@ -97,4 +97,4 @@ public final class ValidationProbeEvent extends IpConnectivityEvent implements P static final SparseArray<String> constants = MessageUtils.findMessageNames( new Class[]{ValidationProbeEvent.class}, new String[]{"PROBE_"}); } -}; +} diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index b27cb32bd4eb..eeb641d33c20 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -81,4 +81,5 @@ interface IUserManager { void clearSeedAccountData(); boolean someUserHasSeedAccount(in String accountName, in String accountType); boolean isManagedProfile(int userId); + boolean isDemoUser(int userId); } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index d74220676ca2..a44a9eed701b 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -859,15 +859,17 @@ public class UserManager { } /** - * Checks if the calling app is running in a demo user. - * <p> - * Caller must hold the MANAGE_USERS permission. + * Checks if the calling app is running in a demo user. When running in a demo user, + * apps can be more helpful to the user, or explain their features in more detail. + * * @return whether the caller is a demo user. - * @hide */ public boolean isDemoUser() { - UserInfo user = getUserInfo(UserHandle.myUserId()); - return user != null && user.isDemo(); + try { + return mService.isDemoUser(UserHandle.myUserId()); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } } /** diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java index bddd8261746b..36ab394f4a79 100644 --- a/core/java/com/android/internal/app/PlatLogoActivity.java +++ b/core/java/com/android/internal/app/PlatLogoActivity.java @@ -54,6 +54,7 @@ import android.widget.ImageView; public class PlatLogoActivity extends Activity { public static final boolean REVEAL_THE_NAME = false; + public static final boolean FINISH = false; FrameLayout mLayout; int mTapCount; @@ -138,7 +139,7 @@ public class PlatLogoActivity extends Activity { } catch (ActivityNotFoundException ex) { Log.e("PlatLogoActivity", "No more eggs."); } - finish(); + if (FINISH) finish(); } }); return true; diff --git a/core/jni/android_app_ApplicationLoaders.cpp b/core/jni/android_app_ApplicationLoaders.cpp index 24ee9591fc2f..3e7c039e2129 100644 --- a/core/jni/android_app_ApplicationLoaders.cpp +++ b/core/jni/android_app_ApplicationLoaders.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#define LOG_TAG "ApplicationLoaders" + #include <nativehelper/ScopedUtfChars.h> #include <nativeloader/native_loader.h> #include <vulkan/vulkan_loader_data.h> @@ -22,10 +24,17 @@ static void setupVulkanLayerPath_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring librarySearchPath) { + android_namespace_t* ns = android::FindNamespaceByClassLoader(env, classLoader); ScopedUtfChars layerPathChars(env, librarySearchPath); + vulkan::LoaderData& loader_data = vulkan::LoaderData::GetInstance(); - loader_data.layer_path = layerPathChars.c_str(); - loader_data.app_namespace = android::FindNamespaceByClassLoader(env, classLoader); + if (loader_data.layer_path.empty()) { + loader_data.layer_path = layerPathChars.c_str(); + loader_data.app_namespace = ns; + } else { + ALOGD("ignored Vulkan layer search path %s for namespace %p", + layerPathChars.c_str(), ns); + } } static const JNINativeMethod g_methods[] = { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 3783dc8fa179..3c71dd9312a5 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -193,6 +193,7 @@ <protected-broadcast android:name="android.btopp.intent.action.OPEN" /> <protected-broadcast android:name="android.btopp.intent.action.OPEN_INBOUND" /> <protected-broadcast android:name="android.btopp.intent.action.TRANSFER_COMPLETE" /> + <protected-broadcast android:name="com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN" /> <protected-broadcast android:name="com.android.bluetooth.pbap.authchall" /> <protected-broadcast android:name="com.android.bluetooth.pbap.userconfirmtimeout" /> <protected-broadcast android:name="com.android.bluetooth.pbap.authresponse" /> diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml index defa83a9b5c6..516f25284282 100644 --- a/core/res/res/drawable-nodpi/platlogo.xml +++ b/core/res/res/drawable-nodpi/platlogo.xml @@ -19,10 +19,10 @@ Copyright (C) 2016 The Android Open Source Project android:viewportWidth="48.0" android:viewportHeight="48.0"> <path - android:fillColor="#FF7E5BBF" + android:fillColor="#FFc7d4b6" android:pathData="M32.0,12.5l0.0,28.0l12.0,-5.0l0.0,-28.0z"/> <path - android:fillColor="#FF7E5BBF" + android:fillColor="#FFfbd3cb" android:pathData="M4.0,40.5l12.0,-5.0l0.0,-11.0l-12.0,-12.0z"/> <path android:fillColor="#40000000" @@ -31,7 +31,7 @@ Copyright (C) 2016 The Android Open Source Project android:fillColor="#40000000" android:pathData="M4.0,12.5l12.0,12.0l0.0,4.0z"/> <path - android:fillColor="#FF55C4F5" + android:fillColor="#FFe0e0d6" android:pathData="M32.0,23.5l-16.0,-16.0l-12.0,5.0l0.0,0.0l12.0,12.0l16.0,16.0l12.0,-5.0l0.0,0.0z"/> </vector> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index ae2a9e4e1b4e..a32faf8a8bce 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4382,7 +4382,7 @@ <!-- Title of the dialog shown when user inactivity times out in retail demo mode [CHAR LIMIT=40] --> <string name="demo_user_inactivity_timeout_title">Reset device?</string> <!-- Warning message shown when user inactivity times out in retail demo mode [CHAR LIMIT=none] --> - <string name="demo_user_inactivity_timeout_countdown">You\'ll lose any changes and the demo will start again in <xliff:g id="timeout" example="9">%1$s</xliff:g> seconds\u2026</string> + <string name="demo_user_inactivity_timeout_countdown">You\u2019ll lose any changes and the demo will start again in <xliff:g id="timeout" example="9">%1$s</xliff:g> seconds\u2026</string> <!-- Text of button to allow user to abort countdown and continue current session in retail demo mode [CHAR LIMIT=40] --> <string name="demo_user_inactivity_timeout_left_button">Cancel</string> <!-- Text of button to allow user to abort countdown and immediately start another session in retail demo mode [CHAR LIMIT=40] --> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 92f049d6e16c..5b2522f97c8b 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -318,6 +318,7 @@ please see themes_device_defaults.xml. <item name="activityChooserViewStyle">@style/Widget.ActivityChooserView</item> <item name="mediaRouteButtonStyle">@style/Widget.DeviceDefault.MediaRouteButton</item> <item name="fragmentBreadCrumbsStyle">@style/Widget.FragmentBreadCrumbs</item> + <item name="contextPopupMenuStyle">?attr/popupMenuStyle</item> <!-- Preference styles --> <item name="preferenceScreenStyle">@style/Preference.PreferenceScreen</item> diff --git a/docs/html/preview/features/picture-in-picture.jd b/docs/html/preview/features/picture-in-picture.jd index c089feb17670..03a17682a0a4 100644 --- a/docs/html/preview/features/picture-in-picture.jd +++ b/docs/html/preview/features/picture-in-picture.jd @@ -220,7 +220,11 @@ in any area that can be obscured by the PIP window.</p> <p>When an activity is in PIP mode, by default it doesn't get input focus. To receive input events while in PIP mode, use -<code>MediaSession.setMediaButtonReceiver()</code>.</p> +{@link android.media.session.MediaSession#setCallback +MediaSession.setCallback()}. For more information on using +{@link android.media.session.MediaSession#setCallback setCallback()} see +<a href="{@docRoot}training/tv/playback/now-playing.html">Displaying +a Now Playing Card</a>.</p> <p>When your app is in PIP mode, video playback in the PIP window can cause audio interference with another app, such as a music player app or voice search @@ -228,4 +232,4 @@ app. To avoid this, request audio focus when you start playing the video, and handle audio focus change notifications, as described in <a href="{@docRoot}training/managing-audio/audio-focus.html">Managing Audio Focus</a>. If you receive notification of audio focus loss when in PIP mode, -pause or stop video playback.</p>
\ No newline at end of file +pause or stop video playback.</p> diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index bcc354c5b736..3dbd2a96b00a 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -699,7 +699,7 @@ public class GradientDrawable extends Drawable { float rad = mStrokePaint.getStrokeWidth(); canvas.saveLayer(mRect.left - rad, mRect.top - rad, mRect.right + rad, mRect.bottom + rad, - mLayerPaint, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG); + mLayerPaint); // don't perform the filter in our individual paints // since the layer will do it for us diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index b35c92612a42..cbefccb955de 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -127,7 +127,8 @@ int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, // operations will be able to store and restore the current clip and transform info, and // quick rejection will be correct (for display lists) - const Rect unmappedBounds(left, top, right, bottom); + Rect unmappedBounds(left, top, right, bottom); + unmappedBounds.roundOut(); // determine clipped bounds relative to previous viewport. Rect visibleBounds = unmappedBounds; diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp index 18171de250d0..9cd504ec0af7 100644 --- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp +++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp @@ -340,6 +340,36 @@ TEST(RecordingCanvas, saveLayer_simple) { EXPECT_EQ(3, count); } +TEST(RecordingCanvas, saveLayer_rounding) { + auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) { + canvas.saveLayerAlpha(10.25f, 10.75f, 89.25f, 89.75f, 128, SaveFlags::ClipToLayer); + canvas.drawRect(20, 20, 80, 80, SkPaint()); + canvas.restore(); + }); + int count = 0; + playbackOps(*dl, [&count](const RecordedOp& op) { + Matrix4 expectedMatrix; + switch(count++) { + case 0: + EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId); + EXPECT_EQ(Rect(10, 10, 90, 90), op.unmappedBounds) << "Expect bounds rounded out"; + break; + case 1: + EXPECT_EQ(RecordedOpId::RectOp, op.opId); + expectedMatrix.loadTranslate(-10, -10, 0); + EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix) << "Expect rounded offset"; + break; + case 2: + EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId); + // Don't bother asserting recording state data - it's not used + break; + default: + ADD_FAILURE(); + } + }); + EXPECT_EQ(3, count); +} + TEST(RecordingCanvas, saveLayer_missingRestore) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer); diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 71d1aaafe45f..542dcedb007c 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -297,8 +297,8 @@ import java.util.Map; Codec-specific data in the format is automatically submitted to the codec upon {@link #start}; you <strong>MUST NOT</strong> submit this data explicitly. If the format did not contain codec specific data, you can choose to submit it using the specified number of buffers in the correct - order, according to the format requirements. Alternately, you can concatenate all codec-specific - data and submit it as a single codec-config buffer. + order, according to the format requirements. In case of H.264 AVC, you can also concatenate all + codec-specific data and submit it as a single codec-config buffer. <p> Android uses the following codec-specific data buffers. These are also required to be set in the track format for proper {@link MediaMuxer} track configuration. Each parameter set and the @@ -355,6 +355,13 @@ import java.util.Map; <td class=NA>Not Used</td> <td class=NA>Not Used</td> </tr> + <tr> + <td>VP9</td> + <td>VP9 <a href="http://wiki.webmproject.org/vp9-codecprivate">CodecPrivate</a> Data + (optional)</td> + <td class=NA>Not Used</td> + <td class=NA>Not Used</td> + </tr> </tbody> </table> @@ -606,6 +613,32 @@ import java.util.Map; Also since {@link android.os.Build.VERSION_CODES#M}, you can change the output Surface dynamically using {@link #setOutputSurface setOutputSurface}. + <h4>Transformations When Rendering onto Surface</h4> + + If the codec is configured into Surface mode, any crop rectangle, {@linkplain + MediaFormat#KEY_ROTATION rotation} and {@linkplain #setVideoScalingMode video scaling + mode} will be automatically applied with one exception: + <p class=note> + Prior to the {@link android.os.Build.VERSION_CODES#M} release, software decoders may not + have applied the rotation when being rendered onto a Surface. Unfortunately, there is no way to + identify software decoders, or if they apply the rotation other than by trying it out. + <p> + There are also some caveats. + <p class=note> + Note that the pixel aspect ratio is not considered when displaying the output onto the + Surface. This means that if you are using {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT} mode, you + must position the output Surface so that it has the proper final display aspect ratio. Conversely, + you can only use {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING} mode for content with + square pixels (pixel aspect ratio or 1:1). + <p class=note> + Note also that as of {@link android.os.Build.VERSION_CODES#N} release, {@link + #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING} mode may not work correctly for videos rotated + by 90 or 270 degrees. + <p class=note> + When setting the video scaling mode, note that it must be reset after each time the output + buffers change. Since the {@link #INFO_OUTPUT_BUFFERS_CHANGED} event is deprecated, you can + do this after each time the output format changes. + <h4>Using an Input Surface</h4> <p> When using an input Surface, there are no accessible input buffers, as buffers are automatically @@ -3055,7 +3088,13 @@ final public class MediaCodec { /** * The content is scaled, maintaining its aspect ratio, the whole - * surface area is used, content may be cropped + * surface area is used, content may be cropped. + * <p class=note> + * This mode is only suitable for content with 1:1 pixel aspect ratio as you cannot + * configure the pixel aspect ratio for a {@link Surface}. + * <p class=note> + * As of {@link android.os.Build.VERSION_CODES#N} release, this mode may not work if + * the video is {@linkplain MediaFormat#KEY_ROTATION rotated} by 90 or 270 degrees. */ public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2; @@ -3070,10 +3109,15 @@ final public class MediaCodec { /** * If a surface has been specified in a previous call to {@link #configure} * specifies the scaling mode to use. The default is "scale to fit". - * <p class=note>The scaling mode may be reset to the <strong>default</strong> each time an + * <p class=note> + * The scaling mode may be reset to the <strong>default</strong> each time an * {@link #INFO_OUTPUT_BUFFERS_CHANGED} event is received from the codec; therefore, the client * must call this method after every buffer change event (and before the first output buffer is - * released for rendering) to ensure consistent scaling mode.</p> + * released for rendering) to ensure consistent scaling mode. + * <p class=note> + * Since the {@link #INFO_OUTPUT_BUFFERS_CHANGED} event is deprecated, this can also be done + * after each {@link #INFO_OUTPUT_FORMAT_CHANGED} event. + * * @throws IllegalArgumentException if mode is not recognized. * @throws IllegalStateException if in the Released state. */ diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 07d1f7542b26..0bfeaed36282 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -541,6 +541,72 @@ public final class MediaCodecInfo { * frame rate}. Use * <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code> * to clear any existing frame rate setting in the format. + * <p> + * + * The following table summarizes the format keys considered by this method. + * + * <table style="width: 0%"> + * <thead> + * <tr> + * <th rowspan=3>OS Version(s)</th> + * <td colspan=3>{@code MediaFormat} keys considered for</th> + * </tr><tr> + * <th>Audio Codecs</th> + * <th>Video Codecs</th> + * <th>Encoders</th> + * </tr> + * </thead> + * <tbody> + * <tr> + * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP}</th> + * <td rowspan=3>{@link MediaFormat#KEY_MIME}<sup>*</sup>,<br> + * {@link MediaFormat#KEY_SAMPLE_RATE},<br> + * {@link MediaFormat#KEY_CHANNEL_COUNT},</td> + * <td>{@link MediaFormat#KEY_MIME}<sup>*</sup>,<br> + * {@link CodecCapabilities#FEATURE_AdaptivePlayback}<sup>D</sup>,<br> + * {@link CodecCapabilities#FEATURE_SecurePlayback}<sup>D</sup>,<br> + * {@link CodecCapabilities#FEATURE_TunneledPlayback}<sup>D</sup>,<br> + * {@link MediaFormat#KEY_WIDTH},<br> + * {@link MediaFormat#KEY_HEIGHT},<br> + * <strong>no</strong> {@code KEY_FRAME_RATE}</td> + * <td rowspan=4>{@link MediaFormat#KEY_BITRATE_MODE},<br> + * {@link MediaFormat#KEY_PROFILE} + * (and/or {@link MediaFormat#KEY_AAC_PROFILE}<sup>~</sup>),<br> + * <!-- {link MediaFormat#KEY_QUALITY},<br> --> + * {@link MediaFormat#KEY_COMPLEXITY} + * (and/or {@link MediaFormat#KEY_FLAC_COMPRESSION_LEVEL}<sup>~</sup>)</td> + * </tr><tr> + * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}</th> + * <td rowspan=2>as above, plus<br> + * {@link MediaFormat#KEY_FRAME_RATE}</td> + * </tr><tr> + * <td>{@link android.os.Build.VERSION_CODES#M}</th> + * </tr><tr> + * <td>{@link android.os.Build.VERSION_CODES#N}</th> + * <td>as above, plus<br> + * {@link MediaFormat#KEY_PROFILE},<br> + * <!-- {link MediaFormat#KEY_MAX_BIT_RATE},<br> --> + * {@link MediaFormat#KEY_BIT_RATE}</td> + * <td>as above, plus<br> + * {@link MediaFormat#KEY_PROFILE},<br> + * {@link MediaFormat#KEY_LEVEL}<sup>+</sup>,<br> + * <!-- {link MediaFormat#KEY_MAX_BIT_RATE},<br> --> + * {@link MediaFormat#KEY_BIT_RATE},<br> + * {@link CodecCapabilities#FEATURE_IntraRefresh}<sup>E</sup></td> + * </tr> + * <tr> + * <td colspan=4> + * <p class=note><strong>Notes:</strong><br> + * *: must be specified; otherwise, method returns {@code false}.<br> + * +: method does not verify that the format parameters are supported + * by the specified level.<br> + * D: decoders only<br> + * E: encoders only<br> + * ~: if both keys are provided values must match + * </td> + * </tr> + * </tbody> + * </table> * * @param format media format with optional feature directives. * @throws IllegalArgumentException if format is not a valid media format. diff --git a/media/java/android/media/MediaCodecList.java b/media/java/android/media/MediaCodecList.java index 42ce5110f134..3cb4cbe99a4b 100644 --- a/media/java/android/media/MediaCodecList.java +++ b/media/java/android/media/MediaCodecList.java @@ -201,6 +201,9 @@ final public class MediaCodecList { * <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code> * to clear any existing frame rate setting in the format. * + * @see MediaCodecList.CodecCapabilities.isFormatSupported for format keys + * considered per android versions when evaluating suitable codecs. + * * @param format A decoder media format with optional feature directives. * @throws IllegalArgumentException if format is not a valid media format. * @throws NullPointerException if format is null. @@ -222,6 +225,9 @@ final public class MediaCodecList { * <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code> * to clear any existing frame rate setting in the format. * + * @see MediaCodecList.CodecCapabilities.isFormatSupported for format keys + * considered per android versions when evaluating suitable codecs. + * * @param format An encoder media format with optional feature directives. * @throws IllegalArgumentException if format is not a valid media format. * @throws NullPointerException if format is null. diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java index 24a400e48189..6f5199b6959c 100644 --- a/media/java/android/media/MediaExtractor.java +++ b/media/java/android/media/MediaExtractor.java @@ -333,7 +333,113 @@ final public class MediaExtractor { /** * Get the track format at the specified index. + * * More detail on the representation can be found at {@link android.media.MediaCodec} + * <p> + * The following table summarizes support for format keys across android releases: + * + * <table style="width: 0%"> + * <thead> + * <tr> + * <th rowspan=2>OS Version(s)</th> + * <td colspan=3>{@code MediaFormat} keys used for</th> + * </tr><tr> + * <th>All Tracks</th> + * <th>Audio Tracks</th> + * <th>Video Tracks</th> + * </tr> + * </thead> + * <tbody> + * <tr> + * <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN}</td> + * <td rowspan=8>{@link MediaFormat#KEY_MIME},<br> + * {@link MediaFormat#KEY_DURATION},<br> + * {@link MediaFormat#KEY_MAX_INPUT_SIZE}</td> + * <td rowspan=5>{@link MediaFormat#KEY_SAMPLE_RATE},<br> + * {@link MediaFormat#KEY_CHANNEL_COUNT},<br> + * {@link MediaFormat#KEY_CHANNEL_MASK},<br> + * gapless playback information<sup>.mp3, .mp4</sup>,<br> + * {@link MediaFormat#KEY_IS_ADTS}<sup>AAC if streaming</sup>,<br> + * codec-specific data<sup>AAC, Vorbis</sup></td> + * <td rowspan=2>{@link MediaFormat#KEY_WIDTH},<br> + * {@link MediaFormat#KEY_HEIGHT},<br> + * codec-specific data<sup>AVC, MPEG4</sup></td> + * </tr><tr> + * <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}</td> + * </tr><tr> + * <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}</td> + * <td rowspan=3>as above, plus<br> + * Pixel aspect ratio information<sup>AVC, *</sup></td> + * </tr><tr> + * <td>{@link android.os.Build.VERSION_CODES#KITKAT}</td> + * </tr><tr> + * <td>{@link android.os.Build.VERSION_CODES#KITKAT_WATCH}</td> + * </tr><tr> + * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP}</td> + * <td rowspan=2>as above, plus<br> + * {@link MediaFormat#KEY_BIT_RATE}<sup>AAC</sup>,<br> + * codec-specific data<sup>Opus</sup></td> + * <td rowspan=2>as above, plus<br> + * {@link MediaFormat#KEY_ROTATION}<sup>.mp4</sup>,<br> + * {@link MediaFormat#KEY_BIT_RATE}<sup>MPEG4</sup>,<br> + * codec-specific data<sup>HEVC</sup></td> + * </tr><tr> + * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}</td> + * </tr><tr> + * <td>{@link android.os.Build.VERSION_CODES#M}</td> + * <td>as above, plus<br> + * gapless playback information<sup>Opus</sup></td> + * <td>as above, plus<br> + * {@link MediaFormat#KEY_FRAME_RATE} (integer)</td> + * </tr><tr> + * <td>{@link android.os.Build.VERSION_CODES#N}</td> + * <td>as above, plus<br> + * {@link MediaFormat#KEY_TRACK_ID},<br> + * <!-- {link MediaFormat#KEY_MAX_BIT_RATE}<sup>#, .mp4</sup>,<br> --> + * {@link MediaFormat#KEY_BIT_RATE}<sup>#, .mp4</sup></td> + * <td>as above, plus<br> + * {@link MediaFormat#KEY_PCM_ENCODING},<br> + * {@link MediaFormat#KEY_PROFILE}<sup>AAC</sup></td> + * <td>as above, plus<br> + * {@link MediaFormat#KEY_HDR_STATIC_INFO}<sup>#, .webm</sup>,<br> + * {@link MediaFormat#KEY_COLOR_STANDARD}<sup>#</sup>,<br> + * {@link MediaFormat#KEY_COLOR_TRANSFER}<sup>#</sup>,<br> + * {@link MediaFormat#KEY_COLOR_RANGE}<sup>#</sup>,<br> + * {@link MediaFormat#KEY_PROFILE}<sup>MPEG2, H.263, MPEG4, AVC, HEVC, VP9</sup>,<br> + * {@link MediaFormat#KEY_LEVEL}<sup>H.263, MPEG4, AVC, HEVC, VP9</sup>,<br> + * codec-specific data<sup>VP9</sup></td> + * </tr> + * <tr> + * <td colspan=4> + * <p class=note><strong>Notes:</strong><br> + * #: container-specified value only.<br> + * .mp4, .webm…: for listed containers<br> + * MPEG4, AAC…: for listed codecs + * </td> + * </tr><tr> + * <td colspan=4> + * <p class=note>Note that that level information contained in the container many times + * does not match the level of the actual bitstream. You may want to clear the level using + * {@code MediaFormat.setString(KEY_LEVEL, null)} before using the track format to find a + * decoder that can play back a particular track. + * </td> + * </tr><tr> + * <td colspan=4> + * <p class=note><strong>*Pixel (sample) aspect ratio</strong> is returned in the following + * keys. The display width can be calculated for example as: + * <p align=center> + * display-width = display-height * crop-width / crop-height * sar-width / sar-height + * </td> + * </tr><tr> + * <th>Format Key</th><th>Value Type</th><th colspan=2>Description</th> + * </tr><tr> + * <td>{@code "sar-width"}</td><td>Integer</td><td colspan=2>Pixel aspect ratio width</td> + * </tr><tr> + * <td>{@code "sar-height"}</td><td>Integer</td><td colspan=2>Pixel aspect ratio height</td> + * </tr> + * </tbody> + * </table> + * */ @NonNull public MediaFormat getTrackFormat(int index) { diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index a2fd0aa8952f..d7a18d97874d 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -554,7 +554,9 @@ public final class MediaFormat { /** * A key describing the desired clockwise rotation on an output surface. * This key is only used when the codec is configured using an output surface. - * The associated value is an integer, representing degrees. + * The associated value is an integer, representing degrees. Supported values + * are 0, 90, 180 or 270. This is an optional field; if not specified, rotation + * defaults to 0. * * @see MediaCodecInfo.CodecCapabilities#profileLevels */ diff --git a/packages/EasterEgg/res/drawable/icon.xml b/packages/EasterEgg/res/drawable/icon.xml index 5e08fcbf8f52..defa83a9b5c6 100644 --- a/packages/EasterEgg/res/drawable/icon.xml +++ b/packages/EasterEgg/res/drawable/icon.xml @@ -14,15 +14,15 @@ Copyright (C) 2016 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="48dp" - android:height="48dp" + android:width="512dp" + android:height="512dp" android:viewportWidth="48.0" android:viewportHeight="48.0"> <path - android:fillColor="#00796B" + android:fillColor="#FF7E5BBF" android:pathData="M32.0,12.5l0.0,28.0l12.0,-5.0l0.0,-28.0z"/> <path - android:fillColor="#00796B" + android:fillColor="#FF7E5BBF" android:pathData="M4.0,40.5l12.0,-5.0l0.0,-11.0l-12.0,-12.0z"/> <path android:fillColor="#40000000" @@ -31,8 +31,7 @@ Copyright (C) 2016 The Android Open Source Project android:fillColor="#40000000" android:pathData="M4.0,12.5l12.0,12.0l0.0,4.0z"/> <path - android:fillColor="#4DB6AC" + android:fillColor="#FF55C4F5" android:pathData="M32.0,23.5l-16.0,-16.0l-12.0,5.0l0.0,0.0l12.0,12.0l16.0,16.0l12.0,-5.0l0.0,0.0z"/> </vector> - diff --git a/packages/EasterEgg/src/com/android/egg/neko/Cat.java b/packages/EasterEgg/src/com/android/egg/neko/Cat.java index 525b035989f9..864b20c73fbf 100644 --- a/packages/EasterEgg/src/com/android/egg/neko/Cat.java +++ b/packages/EasterEgg/src/com/android/egg/neko/Cat.java @@ -16,7 +16,6 @@ package com.android.egg.neko; import android.app.Notification; import android.app.PendingIntent; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.res.Resources; @@ -31,6 +30,8 @@ import java.util.concurrent.ThreadLocalRandom; import com.android.egg.R; public class Cat extends Drawable { + public static final long[] PURR = {0, 40, 20, 40, 20, 40, 20, 40, 20, 40, 20, 40}; + private Random mNotSoRandom; private Bitmap mBitmap; private long mSeed; @@ -198,6 +199,7 @@ public class Cat extends Drawable { .setContentText(getName()) .setContentIntent(PendingIntent.getActivity(context, 0, intent, 0)) .setAutoCancel(true) + .setVibrate(PURR) .addExtras(extras); } diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java b/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java index 8fbab99d8884..88a7968da16c 100644 --- a/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java +++ b/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java @@ -18,22 +18,35 @@ import android.app.Activity; import android.content.ComponentName; import android.content.pm.PackageManager; import android.util.Log; +import android.widget.Toast; public class NekoActivationActivity extends Activity { + private void toastUp(String s) { + Toast toast = Toast.makeText(this, s, Toast.LENGTH_SHORT); + toast.getView().setBackgroundDrawable(null); + toast.show(); + } + @Override public void onStart() { + super.onStart(); + final PackageManager pm = getPackageManager(); final ComponentName cn = new ComponentName(this, NekoTile.class); if (pm.getComponentEnabledSetting(cn) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { if (NekoLand.DEBUG) { Log.v("Neko", "Disabling tile."); } - pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0); + pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP); + toastUp("\uD83D\uDEAB"); } else { if (NekoLand.DEBUG) { Log.v("Neko", "Enabling tile."); } - pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0); + pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, + PackageManager.DONT_KILL_APP); + toastUp("\uD83D\uDC31"); } finish(); } diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java index 999d82d592ea..6140428d353f 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java +++ b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java @@ -838,9 +838,15 @@ public final class PageContentRepository { try (ParcelFileDescriptor source = pipe[0]) { try (ParcelFileDescriptor destination = pipe[1]) { - - mRenderer.renderPage(mPageIndex, bitmap.getWidth(), bitmap.getHeight(), - mRenderSpec.printAttributes, destination); + synchronized (mLock) { + if (mRenderer != null) { + mRenderer.renderPage(mPageIndex, bitmap.getWidth(), + bitmap.getHeight(), mRenderSpec.printAttributes, + destination); + } else { + throw new IllegalStateException("Renderer is disconnected"); + } + } } BitmapSerializeUtils.readBitmapPixels(bitmap, source); diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index 7993131e60ec..548ddf8aeb28 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -16,6 +16,7 @@ import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; import android.os.BatteryManager; import android.os.UserManager; +import android.print.PrintManager; import com.android.internal.util.UserIcons; import com.android.settingslib.drawable.UserIconDrawable; @@ -185,7 +186,8 @@ public class Utils { && sSystemSignature[0].equals(getFirstSignature(pkg))) || pkg.packageName.equals(sPermissionControllerPackageName) || pkg.packageName.equals(sServicesSystemSharedLibPackageName) - || pkg.packageName.equals(sSharedSystemSharedLibPackageName); + || pkg.packageName.equals(sSharedSystemSharedLibPackageName) + || pkg.packageName.equals(PrintManager.PRINT_SPOOLER_PACKAGE_NAME); } private static Signature getFirstSignature(PackageInfo pkg) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index b20f46f6b1f5..7e1deec3429a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -184,7 +184,9 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header, null, false); reloadResources(); + } + public void onBootCompleted() { // When we start, preload the data associated with the previous recent tasks. // We can use a new plan since the caches will be the same. RecentsTaskLoader loader = Recents.getTaskLoader(); @@ -197,10 +199,6 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener loader.loadTasks(mContext, plan, launchOpts); } - public void onBootCompleted() { - // Do nothing - } - public void onConfigurationChanged() { reloadResources(); mDummyStackView.reloadOnConfigurationChange(); 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 94231c6403b0..d5aa69ab3105 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -314,8 +314,12 @@ public class SystemServicesProxy { if (includeFrontMostExcludedTask) { flags |= ActivityManager.RECENT_WITH_EXCLUDED; } - List<ActivityManager.RecentTaskInfo> tasks = mAm.getRecentTasksForUser(numTasksToQuery, - flags, userId); + List<ActivityManager.RecentTaskInfo> tasks = null; + try { + tasks = mAm.getRecentTasksForUser(numTasksToQuery, flags, userId); + } catch (Exception e) { + Log.e(TAG, "Failed to get recent tasks", e); + } // Break early if we can't get a valid set of tasks if (tasks == null) { diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 44b8d3d83df8..1a7a2bf88a88 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -76,6 +76,7 @@ import android.net.RouteInfo; import android.net.UidRange; import android.net.Uri; import android.net.metrics.DefaultNetworkEvent; +import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; import android.os.Binder; import android.os.Build; @@ -454,6 +455,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); + /** * Implements support for the legacy "one network per network type" model. * @@ -2205,7 +2208,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private void linger(NetworkAgentInfo nai) { nai.lingering = true; - NetworkEvent.logEvent(nai.network.netId, NetworkEvent.NETWORK_LINGER); + logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER); nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_LINGER); notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING); } @@ -2219,7 +2222,7 @@ public class ConnectivityService extends IConnectivityManager.Stub nai.networkLingered.clear(); if (!nai.lingering) return; nai.lingering = false; - NetworkEvent.logEvent(nai.network.netId, NetworkEvent.NETWORK_UNLINGER); + logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER); if (VDBG) log("Canceling linger of " + nai.name()); nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED); } @@ -5242,7 +5245,7 @@ public class ConnectivityService extends IConnectivityManager.Stub return new NetworkMonitor(context, handler, nai, defaultRequest); } - private static void logDefaultNetworkEvent(NetworkAgentInfo newNai, NetworkAgentInfo prevNai) { + private void logDefaultNetworkEvent(NetworkAgentInfo newNai, NetworkAgentInfo prevNai) { int newNetid = NETID_UNSET; int prevNetid = NETID_UNSET; int[] transports = new int[0]; @@ -5260,6 +5263,10 @@ public class ConnectivityService extends IConnectivityManager.Stub hadIPv6 = lp.hasGlobalIPv6Address() && lp.hasIPv6DefaultRoute(); } - DefaultNetworkEvent.logEvent(newNetid, transports, prevNetid, hadIPv4, hadIPv6); + mMetricsLog.log(new DefaultNetworkEvent(newNetid, transports, prevNetid, hadIPv4, hadIPv6)); + } + + private void logNetworkEvent(NetworkAgentInfo nai, int evtype) { + mMetricsLog.log(new NetworkEvent(nai.network.netId, evtype)); } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index d9148817881b..ee2fa51c8666 100755 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1148,9 +1148,7 @@ public final class ActiveServices { if (r.binding.service.app != null) { if (r.binding.service.app.whitelistManager) { - // Must reset flag here because on computeOomAdjLocked() the service - // connection will be gone... - r.binding.service.app.whitelistManager = false; + updateWhitelistManagerLocked(r.binding.service.app); } // This could have made the service less important. if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java index 1f3ccf530f71..3ed3d9a29c69 100644 --- a/services/core/java/com/android/server/am/PreBootBroadcaster.java +++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java @@ -19,17 +19,23 @@ package com.android.server.am; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import android.app.AppOpsManager; +import android.app.Notification; +import android.app.NotificationManager; import android.content.ComponentName; +import android.content.Context; import android.content.IIntentReceiver; import android.content.Intent; import android.content.pm.ResolveInfo; import android.os.Bundle; +import android.os.Handler; +import android.os.Message; import android.os.Process; import android.os.UserHandle; import android.util.Slog; import com.android.internal.R; import com.android.internal.util.ProgressReporter; +import com.android.server.UiThread; import java.util.List; @@ -61,16 +67,20 @@ public abstract class PreBootBroadcaster extends IIntentReceiver.Stub { mTargets = mService.mContext.getPackageManager().queryBroadcastReceiversAsUser(mIntent, MATCH_SYSTEM_ONLY, UserHandle.of(userId)); + + mHandler.obtainMessage(MSG_SHOW).sendToTarget(); } public void sendNext() { if (mIndex >= mTargets.size()) { + mHandler.obtainMessage(MSG_HIDE).sendToTarget(); onFinished(); return; } if (!mService.isUserRunning(mUserId, 0)) { Slog.i(TAG, "User " + mUserId + " is no longer running; skipping remaining receivers"); + mHandler.obtainMessage(MSG_HIDE).sendToTarget(); onFinished(); return; } @@ -100,5 +110,44 @@ public abstract class PreBootBroadcaster extends IIntentReceiver.Stub { sendNext(); } + private static final int MSG_SHOW = 1; + private static final int MSG_HIDE = 2; + + private Handler mHandler = new Handler(UiThread.get().getLooper(), null, true) { + @Override + public void handleMessage(Message msg) { + final Context context = mService.mContext; + final NotificationManager notifManager = context + .getSystemService(NotificationManager.class); + + switch (msg.what) { + case MSG_SHOW: + final CharSequence title = context + .getText(R.string.android_upgrading_notification_title); + final CharSequence message = context + .getText(R.string.android_upgrading_notification_body); + final Notification notif = new Notification.Builder(mService.mContext) + .setSmallIcon(R.drawable.stat_sys_adb) + .setWhen(0) + .setOngoing(true) + .setTicker(title) + .setDefaults(0) + .setPriority(Notification.PRIORITY_MAX) + .setColor(context.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentText(message) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .build(); + notifManager.notifyAsUser(TAG, 0, notif, UserHandle.of(mUserId)); + break; + + case MSG_HIDE: + notifManager.cancelAsUser(TAG, 0, UserHandle.of(mUserId)); + break; + } + } + }; + public abstract void onFinished(); } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 6b44f14883e5..d25f2cb76d74 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -99,6 +99,7 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; /** * Helper class for {@link ActivityManagerService} responsible for multi-user functionality. @@ -1057,6 +1058,7 @@ final class UserController { uss.switching = true; mCurWaitingUserSwitchCallbacks = curWaitingUserSwitchCallbacks; } + final AtomicInteger waitingCallbacksCount = new AtomicInteger(observerCount); for (int i = 0; i < observerCount; i++) { try { // Prepend with unique prefix to guarantee that keys are unique @@ -1075,7 +1077,7 @@ final class UserController { } curWaitingUserSwitchCallbacks.remove(name); // Continue switching if all callbacks have been notified - if (curWaitingUserSwitchCallbacks.isEmpty()) { + if (waitingCallbacksCount.decrementAndGet() == 0) { sendContinueUserSwitchLocked(uss, oldUserId, newUserId); } } diff --git a/services/core/java/com/android/server/connectivity/DnsEventListenerService.java b/services/core/java/com/android/server/connectivity/DnsEventListenerService.java index 18ab73100b81..8d206ef90b94 100644 --- a/services/core/java/com/android/server/connectivity/DnsEventListenerService.java +++ b/services/core/java/com/android/server/connectivity/DnsEventListenerService.java @@ -17,15 +17,17 @@ package com.android.server.connectivity; import android.content.Context; -import android.net.metrics.DnsEvent; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; import android.net.NetworkRequest; +import android.net.metrics.DnsEvent; import android.net.metrics.IDnsEventListener; +import android.net.metrics.IpConnectivityLog; import android.util.Log; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import java.io.PrintWriter; @@ -45,12 +47,13 @@ public class DnsEventListenerService extends IDnsEventListener.Stub { private static final boolean DBG = true; private static final boolean VDBG = false; + // TODO: read this constant from system property private static final int MAX_LOOKUPS_PER_DNS_EVENT = 100; // Stores the results of a number of consecutive DNS lookups on the same network. // This class is not thread-safe and it is the responsibility of the service to call its methods // on one thread at a time. - private static class DnsEventBatch { + private class DnsEventBatch { private final int mNetId; private final byte[] mEventTypes = new byte[MAX_LOOKUPS_PER_DNS_EVENT]; @@ -82,7 +85,7 @@ public class DnsEventListenerService extends IDnsEventListener.Stub { byte[] eventTypes = Arrays.copyOf(mEventTypes, mEventCount); byte[] returnCodes = Arrays.copyOf(mReturnCodes, mEventCount); int[] latenciesMs = Arrays.copyOf(mLatenciesMs, mEventCount); - DnsEvent.logEvent(mNetId, eventTypes, returnCodes, latenciesMs); + mMetricsLog.log(new DnsEvent(mNetId, eventTypes, returnCodes, latenciesMs)); maybeLog(String.format("Logging %d results for netId %d", mEventCount, mNetId)); mEventCount = 0; } @@ -96,13 +99,14 @@ public class DnsEventListenerService extends IDnsEventListener.Stub { // Only sorted for ease of debugging. Because we only typically have a handful of networks up // at any given time, performance is not a concern. @GuardedBy("this") - private SortedMap<Integer, DnsEventBatch> mEventBatches = new TreeMap<>(); + private final SortedMap<Integer, DnsEventBatch> mEventBatches = new TreeMap<>(); // We register a NetworkCallback to ensure that when a network disconnects, we flush the DNS // queries we've logged on that network. Because we do not do this periodically, we might lose // up to MAX_LOOKUPS_PER_DNS_EVENT lookup stats on each network when the system is shutting // down. We believe this to be sufficient for now. private final ConnectivityManager mCm; + private final IpConnectivityLog mMetricsLog; private final NetworkCallback mNetworkCallback = new NetworkCallback() { @Override public void onLost(Network network) { @@ -116,11 +120,15 @@ public class DnsEventListenerService extends IDnsEventListener.Stub { }; public DnsEventListenerService(Context context) { + this(context.getSystemService(ConnectivityManager.class), new IpConnectivityLog()); + } + + @VisibleForTesting + public DnsEventListenerService(ConnectivityManager cm, IpConnectivityLog log) { // We are started when boot is complete, so ConnectivityService should already be running. - final NetworkRequest request = new NetworkRequest.Builder() - .clearCapabilities() - .build(); - mCm = context.getSystemService(ConnectivityManager.class); + mCm = cm; + mMetricsLog = log; + final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build(); mCm.registerNetworkCallback(request, mNetworkCallback); } diff --git a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java index 69ef30fbe66a..05f1a6e6a3a4 100644 --- a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java +++ b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java @@ -16,6 +16,7 @@ package com.android.server.connectivity; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.SystemService; import android.app.PendingIntent; @@ -60,17 +61,11 @@ public class MetricsLoggerService extends SystemService { } } - // TODO: read from system property - private final int MAX_NUMBER_OF_EVENTS = 1000; - - // TODO: read from system property - private final int EVENTS_NOTIFICATION_THRESHOLD = 300; - - // TODO: read from system property - private final int THROTTLING_TIME_INTERVAL_MILLIS = 60 * 60 * 1000; // 1 hour - - // TODO: read from system property + // TODO: read these constants from system property + private final int EVENTS_NOTIFICATION_THRESHOLD = 300; + private final int MAX_NUMBER_OF_EVENTS = 1000; private final int THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT = 1000; + private final long THROTTLING_TIME_INTERVAL_MILLIS = DateUtils.HOUR_IN_MILLIS; private int mEventCounter = 0; @@ -127,10 +122,13 @@ public class MetricsLoggerService extends SystemService { mEvents.addLast(e); } + @VisibleForTesting + final MetricsLoggerImpl mBinder = new MetricsLoggerImpl(); + /** * Implementation of the IConnectivityMetricsLogger interface. */ - private final IConnectivityMetricsLogger.Stub mBinder = new IConnectivityMetricsLogger.Stub() { + final class MetricsLoggerImpl extends IConnectivityMetricsLogger.Stub { private final ArrayList<PendingIntent> mPendingIntents = new ArrayList<>(); @@ -223,7 +221,9 @@ public class MetricsLoggerService extends SystemService { } pw.println(); - mDnsListener.dump(pw); + if (mDnsListener != null) { + mDnsListener.dump(pw); + } } public long logEvent(ConnectivityMetricsEvent event) { diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java index ddaebfa0e747..eeddff53e8ce 100644 --- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java @@ -34,8 +34,9 @@ import android.net.NetworkRequest; import android.net.ProxyInfo; import android.net.TrafficStats; import android.net.Uri; -import android.net.metrics.ValidationProbeEvent; +import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; +import android.net.metrics.ValidationProbeEvent; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.util.Stopwatch; @@ -230,6 +231,7 @@ public class NetworkMonitor extends StateMachine { private final WifiManager mWifiManager; private final AlarmManager mAlarmManager; private final NetworkRequest mDefaultRequest; + private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); private boolean mIsCaptivePortalCheckEnabled; private boolean mUseHttps; @@ -311,11 +313,11 @@ public class NetworkMonitor extends StateMachine { transitionTo(mLingeringState); return HANDLED; case CMD_NETWORK_CONNECTED: - NetworkEvent.logEvent(mNetId, NetworkEvent.NETWORK_CONNECTED); + logNetworkEvent(NetworkEvent.NETWORK_CONNECTED); transitionTo(mEvaluatingState); return HANDLED; case CMD_NETWORK_DISCONNECTED: - NetworkEvent.logEvent(mNetId, NetworkEvent.NETWORK_DISCONNECTED); + logNetworkEvent(NetworkEvent.NETWORK_DISCONNECTED); if (mLaunchCaptivePortalAppBroadcastReceiver != null) { mContext.unregisterReceiver(mLaunchCaptivePortalAppBroadcastReceiver); mLaunchCaptivePortalAppBroadcastReceiver = null; @@ -380,10 +382,7 @@ public class NetworkMonitor extends StateMachine { private class ValidatedState extends State { @Override public void enter() { - if (mEvaluationTimer.isRunning()) { - NetworkEvent.logValidated(mNetId, mEvaluationTimer.stop()); - mEvaluationTimer.reset(); - } + maybeLogEvaluationResult(NetworkEvent.NETWORK_VALIDATED); mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_VALID, mNetworkAgentInfo.network.netId, null)); } @@ -530,7 +529,7 @@ public class NetworkMonitor extends StateMachine { } else { final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0); sendMessageDelayed(msg, mReevaluateDelayMs); - NetworkEvent.logEvent(mNetId, NetworkEvent.NETWORK_VALIDATION_FAILED); + logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED); mConnectivityServiceHandler.sendMessage(obtainMessage( EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, mNetId, probeResult.mRedirectUrl)); @@ -590,10 +589,7 @@ public class NetworkMonitor extends StateMachine { @Override public void enter() { - if (mEvaluationTimer.isRunning()) { - NetworkEvent.logCaptivePortalFound(mNetId, mEvaluationTimer.stop()); - mEvaluationTimer.reset(); - } + maybeLogEvaluationResult(NetworkEvent.NETWORK_CAPTIVE_PORTAL_FOUND); // Don't annoy user with sign-in notifications. if (mDontDisplaySigninNotification) return; // Create a CustomIntentReceiver that sends us a @@ -758,11 +754,12 @@ public class NetworkMonitor extends StateMachine { if (!TextUtils.isEmpty(hostToResolve)) { String probeName = ValidationProbeEvent.getProbeName(ValidationProbeEvent.PROBE_DNS); final Stopwatch dnsTimer = new Stopwatch().start(); + int dnsResult; + long dnsLatency; try { InetAddress[] addresses = mNetworkAgentInfo.network.getAllByName(hostToResolve); - long dnsLatency = dnsTimer.stop(); - ValidationProbeEvent.logEvent(mNetId, dnsLatency, - ValidationProbeEvent.PROBE_DNS, ValidationProbeEvent.DNS_SUCCESS); + dnsResult = ValidationProbeEvent.DNS_SUCCESS; + dnsLatency = dnsTimer.stop(); final StringBuffer connectInfo = new StringBuffer(", " + hostToResolve + "="); for (InetAddress address : addresses) { connectInfo.append(address.getHostAddress()); @@ -770,11 +767,11 @@ public class NetworkMonitor extends StateMachine { } validationLog(probeName + " OK " + dnsLatency + "ms" + connectInfo); } catch (UnknownHostException e) { - long dnsLatency = dnsTimer.stop(); - ValidationProbeEvent.logEvent(mNetId, dnsLatency, - ValidationProbeEvent.PROBE_DNS, ValidationProbeEvent.DNS_FAILURE); + dnsResult = ValidationProbeEvent.DNS_FAILURE; + dnsLatency = dnsTimer.stop(); validationLog(probeName + " FAIL " + dnsLatency + "ms, " + hostToResolve); } + logValidationProbe(dnsLatency, ValidationProbeEvent.PROBE_DNS, dnsResult); } CaptivePortalProbeResult result; @@ -855,7 +852,7 @@ public class NetworkMonitor extends StateMachine { urlConnection.disconnect(); } } - ValidationProbeEvent.logEvent(mNetId, probeTimer.stop(), probeType, httpResponseCode); + logValidationProbe(probeTimer.stop(), probeType, httpResponseCode); return new CaptivePortalProbeResult(httpResponseCode, redirectUrl); } @@ -1012,4 +1009,19 @@ public class NetworkMonitor extends StateMachine { protected WakeupMessage makeWakeupMessage(Context c, Handler h, String s, int i) { return new WakeupMessage(c, h, s, i); } + + private void logNetworkEvent(int evtype) { + mMetricsLog.log(new NetworkEvent(mNetId, evtype)); + } + + private void maybeLogEvaluationResult(int evtype) { + if (mEvaluationTimer.isRunning()) { + mMetricsLog.log(new NetworkEvent(mNetId, evtype, mEvaluationTimer.stop())); + mEvaluationTimer.reset(); + } + } + + private void logValidationProbe(long durationMs, int probeType, int probeResult) { + mMetricsLog.log(new ValidationProbeEvent(mNetId, durationMs, probeType, probeResult)); + } } diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java index 804be4ea1ae7..c371f9705285 100644 --- a/services/core/java/com/android/server/content/SyncOperation.java +++ b/services/core/java/com/android/server/content/SyncOperation.java @@ -197,7 +197,7 @@ public class SyncOperation { } else if (value instanceof Boolean) { syncExtrasBundle.putBoolean(key, (Boolean) value); } else if (value instanceof Float) { - syncExtrasBundle.putDouble(key, (Double) value); + syncExtrasBundle.putDouble(key, (double) (float) value); } else if (value instanceof Double) { syncExtrasBundle.putDouble(key, (Double) value); } else if (value instanceof String) { diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java index 14e4bc66199d..be8e300c9352 100644 --- a/services/core/java/com/android/server/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java @@ -1019,14 +1019,12 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe } } + /*** + * @param opPackageName the name of the calling package + * @return authenticator id for the current user + */ public long getAuthenticatorId(String opPackageName) { - if (canUseFingerprint(opPackageName, false /* foregroundOnly */, - Binder.getCallingUid(), Binder.getCallingPid())) { - return mCurrentAuthenticatorId; - } else { - Slog.w(TAG, "Client isn't current, returning authenticator_id=0"); - } - return 0; + return mCurrentAuthenticatorId; } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 9209d3d4b54e..1206263fb281 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1997,6 +1997,7 @@ public class NotificationManagerService extends SystemService { android.Manifest.permission.MANAGE_NOTIFICATIONS)) { return; } + checkCallerIsSameApp(pkg); if (!checkPolicyAccess(pkg)) { Slog.w(TAG, "Notification policy access denied calling " + method); throw new SecurityException("Notification policy access denied"); @@ -2577,7 +2578,22 @@ public class NotificationManagerService extends SystemService { + " id=" + id + " notification=" + notification); } - markAsSentFromNotification(notification); + // Whitelist pending intents. + if (notification.allPendingIntents != null) { + final int intentCount = notification.allPendingIntents.size(); + if (intentCount > 0) { + final ActivityManagerInternal am = LocalServices + .getService(ActivityManagerInternal.class); + final long duration = LocalServices.getService( + DeviceIdleController.LocalService.class).getNotificationWhitelistDuration(); + for (int i = 0; i < intentCount; i++) { + PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i); + if (pendingIntent != null) { + am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration); + } + } + } + } // Sanitize inputs notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, @@ -2593,40 +2609,6 @@ public class NotificationManagerService extends SystemService { idOut[0] = id; } - private static void markAsSentFromNotification(Notification notification) { - final ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class); - final long duration = LocalServices.getService(DeviceIdleController.LocalService.class) - .getNotificationWhitelistDuration(); - - if (notification.contentIntent != null) { - am.setPendingIntentWhitelistDuration(notification.contentIntent.getTarget(), duration); - } - if (notification.deleteIntent != null) { - am.setPendingIntentWhitelistDuration(notification.deleteIntent.getTarget(), duration); - } - if (notification.fullScreenIntent != null) { - am.setPendingIntentWhitelistDuration(notification.fullScreenIntent.getTarget(), - duration); - } - if (notification.actions != null) { - for (Notification.Action action: notification.actions) { - if (action.actionIntent == null) { - continue; - } - am.setPendingIntentWhitelistDuration(action.actionIntent.getTarget(), duration); - } - } - if (notification.extrasPendingIntents != null) { - final int intentCount = notification.extrasPendingIntents.size(); - for (int i = 0; i < intentCount; i++) { - PendingIntent pendingIntent = notification.extrasPendingIntents.valueAt(i); - if (pendingIntent != null) { - am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration); - } - } - } - } - private class EnqueueNotificationRunnable implements Runnable { private final NotificationRecord r; private final int userId; @@ -3674,6 +3656,10 @@ public class NotificationManagerService extends SystemService { if (isCallerSystem()) { return; } + checkCallerIsSameApp(pkg); + } + + private static void checkCallerIsSameApp(String pkg) { final int uid = Binder.getCallingUid(); try { ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index d128252e8063..bbffd328eecd 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -456,7 +456,6 @@ public class UserManagerService extends IUserManager.Stub { setUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, true, currentGuestUser.id); } - maybeInitializeDemoMode(UserHandle.USER_SYSTEM); mContext.registerReceiver(mDisableQuietModeCallback, new IntentFilter(ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK), null, mHandler); @@ -869,12 +868,25 @@ public class UserManagerService extends IUserManager.Stub { } } synchronized (mUsersLock) { - UserInfo userInfo = getUserInfoLU(userId); + UserInfo userInfo = getUserInfoLU(userId); return userInfo != null && userInfo.isManagedProfile(); } } @Override + public boolean isDemoUser(int userId) { + int callingUserId = UserHandle.getCallingUserId(); + if (callingUserId != userId && !hasManageUsersPermission()) { + throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId + + " is a demo user"); + } + synchronized (mUsersLock) { + UserInfo userInfo = getUserInfoLU(userId); + return userInfo != null && userInfo.isDemo(); + } + } + + @Override public boolean isRestricted() { synchronized (mUsersLock) { return getUserInfoLU(UserHandle.getCallingUserId()).isRestricted(); @@ -2901,7 +2913,7 @@ public class UserManagerService extends IUserManager.Stub { } private void maybeInitializeDemoMode(int userId) { - if (UserManager.isDeviceInDemoMode(mContext)) { + if (UserManager.isDeviceInDemoMode(mContext) && userId != UserHandle.USER_SYSTEM) { String demoLauncher = mContext.getResources().getString( com.android.internal.R.string.config_demoModeLauncherComponent); diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index edcc32e5b587..b9cb38b242c0 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -134,6 +134,9 @@ class AppWindowToken extends WindowToken { boolean mAppStopped; int mPendingRelaunchCount; + private ArrayList<WindowSurfaceController.SurfaceControlWithBackground> mSurfaceViewBackgrounds = + new ArrayList<WindowSurfaceController.SurfaceControlWithBackground>(); + ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>(); ArrayDeque<Configuration> mFrozenMergedConfig = new ArrayDeque<>(); @@ -734,6 +737,36 @@ class AppWindowToken extends WindowToken { service.mWindowPlacerLocked.performSurfacePlacement(); } + void addSurfaceViewBackground(WindowSurfaceController.SurfaceControlWithBackground background) { + mSurfaceViewBackgrounds.add(background); + } + + void removeSurfaceViewBackground(WindowSurfaceController.SurfaceControlWithBackground background) { + mSurfaceViewBackgrounds.remove(background); + updateSurfaceViewBackgroundVisibilities(); + } + + // We use DimLayers behind SurfaceViews to prevent holes while resizing and creating. + // However, we need to ensure one SurfaceView doesn't cover another when they are both placed + // below the main app window (as traditionally a SurfaceView which is never drawn + // to is totally translucent). So we look at all our SurfaceView backgrounds and only enable + // the background for the SurfaceView with lowest Z order + void updateSurfaceViewBackgroundVisibilities() { + WindowSurfaceController.SurfaceControlWithBackground bottom = null; + int bottomLayer = Integer.MAX_VALUE; + for (int i = 0; i < mSurfaceViewBackgrounds.size(); i++) { + WindowSurfaceController.SurfaceControlWithBackground sc = mSurfaceViewBackgrounds.get(i); + if (sc.mVisible && sc.mLayer < bottomLayer) { + bottomLayer = sc.mLayer; + bottom = sc; + } + } + for (int i = 0; i < mSurfaceViewBackgrounds.size(); i++) { + WindowSurfaceController.SurfaceControlWithBackground sc = mSurfaceViewBackgrounds.get(i); + sc.updateBackgroundVisibility(sc != bottom); + } + } + @Override void dump(PrintWriter pw, String prefix) { super.dump(pw, prefix); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index b8accf547686..84173d26ef8b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3003,8 +3003,8 @@ public class WindowManagerService extends IWindowManager.Stub if (win.mAttrs.type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; } - if (win.isWinVisibleLw() && !winAnimator.isAnimationSet() - && winAnimator.applyAnimationLocked(transit, false)) { + if (win.isWinVisibleLw() && (winAnimator.isAnimationSet() || + winAnimator.applyAnimationLocked(transit, false))) { focusMayChange = isDefaultDisplay; win.mAnimatingExit = true; win.mWinAnimator.mAnimating = true; @@ -3170,6 +3170,7 @@ public class WindowManagerService extends IWindowManager.Stub // frozen, there is no reason to animate and it can cause strange // artifacts when we unfreeze the display if some different animation // is running. + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WM#applyAnimationLocked"); if (okToDisplay()) { DisplayInfo displayInfo = getDefaultDisplayInfoLocked(); final int width = displayInfo.appWidth; @@ -3221,6 +3222,7 @@ public class WindowManagerService extends IWindowManager.Stub } else { atoken.mAppAnimator.clearAnimation(); } + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); return atoken.mAppAnimator.animation != null; } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 6c554954c6dc..e374ee91389f 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -55,6 +55,7 @@ import android.graphics.RectF; import android.graphics.Region; import android.os.Debug; import android.os.RemoteException; +import android.os.Trace; import android.util.Slog; import android.view.DisplayInfo; import android.view.MagnificationSpec; @@ -1863,6 +1864,7 @@ class WindowStateAnimator { // frozen, there is no reason to animate and it can cause strange // artifacts when we unfreeze the display if some different animation // is running. + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#applyAnimationLocked"); if (mService.okToDisplay()) { int anim = mPolicy.selectAnimationLw(mWin, transit); int attr = -1; @@ -1902,6 +1904,8 @@ class WindowStateAnimator { } else { clearAnimation(); } + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); + if (mWin.mAttrs.type == TYPE_INPUT_METHOD) { mService.adjustForImeIfNeeded(mWin.mDisplayContent); if (isEntrance) { diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index 3121415c4811..c77e5725a707 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -84,9 +84,10 @@ class WindowSurfaceController { // to a black-out layer placed one Z-layer below the surface. // This prevents holes to whatever app/wallpaper is underneath. if (animator.mWin.isChildWindow() && - animator.mWin.mSubLayer < 0) { + animator.mWin.mSubLayer < 0 && + animator.mWin.mAppToken != null) { mSurfaceControl = new SurfaceControlWithBackground(s, - name, w, h, format, flags); + name, w, h, format, flags, animator.mWin.mAppToken); } else if (DEBUG_SURFACE_TRACE) { mSurfaceControl = new SurfaceTrace( s, name, w, h, format, flags); @@ -758,18 +759,25 @@ class WindowSurfaceController { } } - private static class SurfaceControlWithBackground extends SurfaceControl { + class SurfaceControlWithBackground extends SurfaceControl { private SurfaceControl mBackgroundControl; private boolean mOpaque = true; - private boolean mVisible = false; + private boolean mAppForcedInvisible = false; + private AppWindowToken mAppToken; + public boolean mVisible = false; + public int mLayer = -1; public SurfaceControlWithBackground(SurfaceSession s, - String name, int w, int h, int format, int flags) + String name, int w, int h, int format, int flags, + AppWindowToken token) throws OutOfResourcesException { super(s, name, w, h, format, flags); mBackgroundControl = new SurfaceControl(s, name, w, h, PixelFormat.OPAQUE, flags | SurfaceControl.FX_SURFACE_DIM); mOpaque = (flags & SurfaceControl.OPAQUE) != 0; + mAppToken = token; + + mAppToken.addSurfaceViewBackground(this); } @Override @@ -782,6 +790,10 @@ class WindowSurfaceController { public void setLayer(int zorder) { super.setLayer(zorder); mBackgroundControl.setLayer(zorder - 1); + if (mLayer != zorder) { + mLayer = zorder; + mAppToken.updateSurfaceViewBackgroundVisibilities(); + } } @Override @@ -818,7 +830,7 @@ class WindowSurfaceController { public void setOpaque(boolean isOpaque) { super.setOpaque(isOpaque); mOpaque = isOpaque; - updateBackgroundVisibility(); + updateBackgroundVisibility(mAppForcedInvisible); } @Override @@ -834,23 +846,28 @@ class WindowSurfaceController { @Override public void hide() { - mVisible = false; super.hide(); - updateBackgroundVisibility(); + if (mVisible) { + mVisible = false; + mAppToken.updateSurfaceViewBackgroundVisibilities(); + } } @Override public void show() { - mVisible = true; super.show(); - updateBackgroundVisibility(); + if (!mVisible) { + mVisible = true; + mAppToken.updateSurfaceViewBackgroundVisibilities(); + } } @Override public void destroy() { super.destroy(); mBackgroundControl.destroy(); - } + mAppToken.removeSurfaceViewBackground(this); + } @Override public void release() { @@ -870,8 +887,9 @@ class WindowSurfaceController { mBackgroundControl.deferTransactionUntil(handle, frame); } - private void updateBackgroundVisibility() { - if (mOpaque && mVisible) { + void updateBackgroundVisibility(boolean forcedInvisible) { + mAppForcedInvisible = forcedInvisible; + if (mOpaque && mVisible && !mAppForcedInvisible) { mBackgroundControl.show(); } else { mBackgroundControl.hide(); diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index 359063c80dbf..e5f972886ccf 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -1074,6 +1074,8 @@ class WindowSurfacePlacer { if (!transitionGoodToGo(appsCount)) { return 0; } + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady"); + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); int transit = mService.mAppTransition.getAppTransition(); if (mService.mSkipAppTransitionAnimation) { @@ -1207,6 +1209,9 @@ class WindowSurfacePlacer { true /*updateInputWindows*/); mService.mFocusMayChange = false; mService.notifyActivityDrawnForKeyguard(); + + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); + return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index b5280165bb39..c42d4617b179 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -8193,6 +8193,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public SystemUpdatePolicy getSystemUpdatePolicy() { + if (UserManager.isDeviceInDemoMode(mContext)) { + // Pretending to have an automatic update policy when the device is in retail demo + // mode. This will allow the device to download and install an ota without + // any user interaction. + return SystemUpdatePolicy.createAutomaticInstallPolicy(); + } synchronized (this) { SystemUpdatePolicy policy = mOwners.getSystemUpdatePolicy(); if (policy != null && !policy.isValid()) { diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java index 96c852bf55f5..5852626db83b 100644 --- a/services/net/java/android/net/dhcp/DhcpClient.java +++ b/services/net/java/android/net/dhcp/DhcpClient.java @@ -30,6 +30,7 @@ import android.net.DhcpResults; import android.net.InterfaceConfiguration; import android.net.LinkAddress; import android.net.NetworkUtils; +import android.net.metrics.IpConnectivityLog; import android.net.metrics.DhcpClientEvent; import android.net.metrics.DhcpErrorEvent; import android.os.Message; @@ -163,6 +164,7 @@ public class DhcpClient extends StateMachine { // System services / libraries we use. private final Context mContext; private final Random mRandom; + private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); // Sockets. // - We use a packet socket to receive, because servers send us packets bound for IP addresses @@ -356,14 +358,14 @@ public class DhcpClient extends StateMachine { } catch (IOException|ErrnoException e) { if (!mStopped) { Log.e(TAG, "Read error", e); - DhcpErrorEvent.logReceiveError(mIfaceName); + logError(DhcpErrorEvent.RECEIVE_ERROR); } } catch (DhcpPacket.ParseException e) { Log.e(TAG, "Can't parse packet: " + e.getMessage()); if (PACKET_DBG) { Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length)); } - DhcpErrorEvent.logParseError(mIfaceName, e.errorCode); + logError(e.errorCode); } } if (DBG) Log.d(TAG, "Receive thread stopped"); @@ -493,7 +495,7 @@ public class DhcpClient extends StateMachine { @Override public void enter() { if (STATE_DBG) Log.d(TAG, "Entering state " + getName()); - DhcpClientEvent.logStateEvent(mIfaceName, getName()); + mMetricsLog.log(new DhcpClientEvent(mIfaceName, getName())); } private String messageName(int what) { @@ -977,4 +979,8 @@ public class DhcpClient extends StateMachine { class DhcpRebootingState extends LoggingState { } + + private void logError(int errorCode) { + mMetricsLog.log(new DhcpErrorEvent(mIfaceName, errorCode)); + } } diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java index cece6c8c0cac..86e15182165d 100644 --- a/services/net/java/android/net/ip/IpManager.java +++ b/services/net/java/android/net/ip/IpManager.java @@ -31,6 +31,7 @@ import android.net.ProxyInfo; import android.net.RouteInfo; import android.net.StaticIpConfiguration; import android.net.dhcp.DhcpClient; +import android.net.metrics.IpConnectivityLog; import android.net.metrics.IpManagerEvent; import android.os.INetworkManagementService; import android.os.Message; @@ -393,6 +394,7 @@ public class IpManager extends StateMachine { private final WakeupMessage mProvisioningTimeoutAlarm; private final WakeupMessage mDhcpActionTimeoutAlarm; private final LocalLog mLocalLog; + private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); private NetworkInterface mNetworkInterface; @@ -634,8 +636,8 @@ public class IpManager extends StateMachine { private void recordMetric(final int type) { if (mStartTimeMillis <= 0) { Log.wtf(mTag, "Start time undefined!"); } - IpManagerEvent.logEvent(type, mInterfaceName, - SystemClock.elapsedRealtime() - mStartTimeMillis); + final long duration = SystemClock.elapsedRealtime() - mStartTimeMillis; + mMetricsLog.log(new IpManagerEvent(mInterfaceName, type, duration)); } // For now: use WifiStateMachine's historical notion of provisioned. diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java index 27600a768896..afb644f3546b 100644 --- a/services/net/java/android/net/ip/IpReachabilityMonitor.java +++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java @@ -24,6 +24,7 @@ import android.net.LinkProperties; import android.net.LinkProperties.ProvisioningChange; import android.net.ProxyInfo; import android.net.RouteInfo; +import android.net.metrics.IpConnectivityLog; import android.net.metrics.IpReachabilityEvent; import android.net.netlink.NetlinkConstants; import android.net.netlink.NetlinkErrorMessage; @@ -151,6 +152,7 @@ public class IpReachabilityMonitor { private final Callback mCallback; private final NetlinkSocketObserver mNetlinkSocketObserver; private final Thread mObserverThread; + private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); @GuardedBy("mLock") private LinkProperties mLinkProperties = new LinkProperties(); // TODO: consider a map to a private NeighborState class holding more @@ -359,7 +361,6 @@ public class IpReachabilityMonitor { } if (delta == ProvisioningChange.LOST_PROVISIONING) { - IpReachabilityEvent.logProvisioningLost(mInterfaceName); final String logMsg = "FAILURE: LOST_PROVISIONING, " + msg; Log.w(TAG, logMsg); if (mCallback != null) { @@ -367,8 +368,9 @@ public class IpReachabilityMonitor { // an InetAddress argument. mCallback.notifyLost(ip, logMsg); } + logEvent(IpReachabilityEvent.PROVISIONING_LOST, 0); } else { - IpReachabilityEvent.logNudFailed(mInterfaceName); + logEvent(IpReachabilityEvent.NUD_FAILED, 0); } } @@ -393,7 +395,7 @@ public class IpReachabilityMonitor { break; } final int returnValue = probeNeighbor(mInterfaceIndex, target); - IpReachabilityEvent.logProbeEvent(mInterfaceName, returnValue); + logEvent(IpReachabilityEvent.PROBE, returnValue); } } @@ -413,6 +415,11 @@ public class IpReachabilityMonitor { return (numUnicastProbes * retransTimeMs) + gracePeriodMs; } + private void logEvent(int probeType, int errorCode) { + int eventType = probeType | (errorCode & 0xff ); + mMetricsLog.log(new IpReachabilityEvent(mInterfaceName, eventType)); + } + // TODO: simplify the number of objects by making this extend Thread. private final class NetlinkSocketObserver implements Runnable { private NetlinkSocket mSocket; diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java index 7a3ebf422a8a..05301c1cb5f2 100644 --- a/services/print/java/com/android/server/print/UserState.java +++ b/services/print/java/com/android/server/print/UserState.java @@ -764,9 +764,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, public void updateIfNeededLocked() { throwIfDestroyedLocked(); - if (readConfigurationLocked()) { - onConfigurationChangedLocked(); - } + readConfigurationLocked(); + onConfigurationChangedLocked(); } public void destroyLocked() { @@ -841,14 +840,12 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, pw.println(); } - private boolean readConfigurationLocked() { - boolean somethingChanged = false; - somethingChanged |= readInstalledPrintServicesLocked(); - somethingChanged |= readDisabledPrintServicesLocked(); - return somethingChanged; + private void readConfigurationLocked() { + readInstalledPrintServicesLocked(); + readDisabledPrintServicesLocked(); } - private boolean readInstalledPrintServicesLocked() { + private void readInstalledPrintServicesLocked() { Set<PrintServiceInfo> tempPrintServices = new HashSet<PrintServiceInfo>(); List<ResolveInfo> installedServices = mContext.getPackageManager() @@ -872,39 +869,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, tempPrintServices.add(PrintServiceInfo.create(installedService, mContext)); } - boolean someServiceChanged = false; - - if (tempPrintServices.size() != mInstalledServices.size()) { - someServiceChanged = true; - } else { - for (PrintServiceInfo newService: tempPrintServices) { - final int oldServiceIndex = mInstalledServices.indexOf(newService); - if (oldServiceIndex < 0) { - someServiceChanged = true; - break; - } - // PrintServiceInfo#equals compares only the id not all members, - // so we are also comparing the members coming from meta-data. - PrintServiceInfo oldService = mInstalledServices.get(oldServiceIndex); - if (!TextUtils.equals(oldService.getAddPrintersActivityName(), - newService.getAddPrintersActivityName()) - || !TextUtils.equals(oldService.getAdvancedOptionsActivityName(), - newService.getAdvancedOptionsActivityName()) - || !TextUtils.equals(oldService.getSettingsActivityName(), - newService.getSettingsActivityName())) { - someServiceChanged = true; - break; - } - } - } - - if (someServiceChanged) { - mInstalledServices.clear(); - mInstalledServices.addAll(tempPrintServices); - return true; - } - - return false; + mInstalledServices.clear(); + mInstalledServices.addAll(tempPrintServices); } /** @@ -944,16 +910,14 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, * * @return true if the state changed. */ - private boolean readDisabledPrintServicesLocked() { + private void readDisabledPrintServicesLocked() { Set<ComponentName> tempDisabledServiceNameSet = new HashSet<ComponentName>(); readPrintServicesFromSettingLocked(Settings.Secure.DISABLED_PRINT_SERVICES, tempDisabledServiceNameSet); if (!tempDisabledServiceNameSet.equals(mDisabledServices)) { mDisabledServices.clear(); mDisabledServices.addAll(tempDisabledServiceNameSet); - return true; } - return false; } private void readPrintServicesFromSettingLocked(String setting, diff --git a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java index 51b1e38237a1..d858e822cdbe 100644 --- a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java +++ b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java @@ -106,7 +106,7 @@ public class RetailDemoModeService extends SystemService { private PendingIntent mResetDemoPendingIntent; private CameraManager mCameraManager; private String[] mCameraIdsWithFlash; - private Configuration mPrimaryUserConfiguration; + private Configuration mSystemUserConfiguration; final Object mActivityLock = new Object(); // Whether the newly created demo user has interacted with the screen yet @@ -179,8 +179,8 @@ public class RetailDemoModeService extends SystemService { private void showInactivityCountdownDialog() { UserInactivityCountdownDialog dialog = new UserInactivityCountdownDialog(getContext(), WARNING_DIALOG_TIMEOUT, MILLIS_PER_SECOND); - dialog.setPositiveButtonClickListener(null); - dialog.setNegativeButtonClickListener(new DialogInterface.OnClickListener() { + dialog.setNegativeButtonClickListener(null); + dialog.setPositiveButtonClickListener(new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mHandler.sendEmptyMessage(MSG_START_NEW_SESSION); @@ -366,12 +366,12 @@ public class RetailDemoModeService extends SystemService { } } - private Configuration getPrimaryUsersConfiguration() { - if (mPrimaryUserConfiguration == null) { + private Configuration getSystemUsersConfiguration() { + if (mSystemUserConfiguration == null) { Settings.System.getConfiguration(getContext().getContentResolver(), - mPrimaryUserConfiguration = new Configuration()); + mSystemUserConfiguration = new Configuration()); } - return mPrimaryUserConfiguration; + return mSystemUserConfiguration; } @Override @@ -424,10 +424,11 @@ public class RetailDemoModeService extends SystemService { mWakeLock.acquire(); } mCurrentUserId = userId; - mNm.notifyAsUser(TAG, 1, createResetNotification(), UserHandle.of(userId)); + mAmi.updatePersistentConfigurationForUser(getSystemUsersConfiguration(), userId); turnOffAllFlashLights(); muteVolumeStreams(); - mAmi.updatePersistentConfigurationForUser(getPrimaryUsersConfiguration(), userId); + mNm.notifyAsUser(TAG, 1, createResetNotification(), UserHandle.of(userId)); + synchronized (mActivityLock) { mUserUntouched = true; } diff --git a/services/retaildemo/java/com/android/server/retaildemo/UserInactivityCountdownDialog.java b/services/retaildemo/java/com/android/server/retaildemo/UserInactivityCountdownDialog.java index e6548b79f9a1..d14f4eb33dc5 100644 --- a/services/retaildemo/java/com/android/server/retaildemo/UserInactivityCountdownDialog.java +++ b/services/retaildemo/java/com/android/server/retaildemo/UserInactivityCountdownDialog.java @@ -20,8 +20,6 @@ import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.os.CountDownTimer; -import android.view.LayoutInflater; -import android.view.View; import android.view.WindowManager; import android.widget.TextView; @@ -30,25 +28,23 @@ import com.android.internal.R; public class UserInactivityCountdownDialog extends AlertDialog { private OnCountDownExpiredListener mOnCountDownExpiredListener; - private View mDialogView; private CountDownTimer mCountDownTimer; private long mCountDownDuration; private long mRefreshInterval; UserInactivityCountdownDialog(Context context, long duration, long refreshInterval) { super(context); - mCountDownDuration = duration; mRefreshInterval = refreshInterval; - mDialogView = LayoutInflater.from(context).inflate(R.layout.alert_dialog, null); - String msg = context.getString(R.string.demo_user_inactivity_timeout_countdown, duration); - getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + + getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); WindowManager.LayoutParams attrs = getWindow().getAttributes(); attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; getWindow().setAttributes(attrs); + setTitle(R.string.demo_user_inactivity_timeout_title); - setView(mDialogView); - setMessage(msg); + setMessage(getContext().getString(R.string.demo_user_inactivity_timeout_countdown, + duration)); } public void setOnCountDownExpiredListener( @@ -58,30 +54,31 @@ public class UserInactivityCountdownDialog extends AlertDialog { public void setPositiveButtonClickListener(OnClickListener onClickListener) { setButton(Dialog.BUTTON_POSITIVE, - getContext().getString(R.string.demo_user_inactivity_timeout_left_button), + getContext().getString(R.string.demo_user_inactivity_timeout_right_button), onClickListener); } public void setNegativeButtonClickListener(OnClickListener onClickListener) { setButton(Dialog.BUTTON_NEGATIVE, - getContext().getString(R.string.demo_user_inactivity_timeout_right_button), + getContext().getString(R.string.demo_user_inactivity_timeout_left_button), onClickListener); } @Override public void show() { super.show(); - mDialogView.post(new Runnable() { + final TextView messageView = (TextView) findViewById(R.id.message); + messageView.post(new Runnable() { @Override public void run() { mCountDownTimer = new CountDownTimer(mCountDownDuration, mRefreshInterval) { @Override public void onTick(long millisUntilFinished) { - String msg = getContext().getResources().getString( + String msg = getContext().getString( R.string.demo_user_inactivity_timeout_countdown, millisUntilFinished / 1000); - ((TextView) mDialogView.findViewById(R.id.message)).setText(msg); + messageView.setText(msg); } @Override @@ -96,8 +93,7 @@ public class UserInactivityCountdownDialog extends AlertDialog { } @Override - public void dismiss() { - super.dismiss(); + public void onStop() { if (mCountDownTimer != null) { mCountDownTimer.cancel(); } diff --git a/services/tests/servicestests/src/android/net/metrics/IpConnectivityLogTest.java b/services/tests/servicestests/src/android/net/metrics/IpConnectivityLogTest.java new file mode 100644 index 000000000000..1433f959898c --- /dev/null +++ b/services/tests/servicestests/src/android/net/metrics/IpConnectivityLogTest.java @@ -0,0 +1,162 @@ +/* + * 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 android.net.metrics; + +import android.os.Bundle; +import android.os.Parcel; +import android.net.ConnectivityMetricsEvent; +import android.net.IConnectivityMetricsLogger; + +import junit.framework.TestCase; +import org.junit.Before; +import org.junit.Test; + +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; + +public class IpConnectivityLogTest extends TestCase { + + // use same Parcel object everywhere for pointer equality + static final Bundle FAKE_EV = new Bundle(); + + @Mock IConnectivityMetricsLogger mService; + ArgumentCaptor<ConnectivityMetricsEvent> evCaptor; + + IpConnectivityLog mLog; + + public void setUp() { + MockitoAnnotations.initMocks(this); + evCaptor = ArgumentCaptor.forClass(ConnectivityMetricsEvent.class); + mLog = new IpConnectivityLog(mService); + } + + public void testLogEvents() throws Exception { + assertTrue(mLog.log(1, FAKE_EV)); + assertTrue(mLog.log(2, FAKE_EV)); + assertTrue(mLog.log(3, FAKE_EV)); + + List<ConnectivityMetricsEvent> gotEvents = verifyEvents(3); + assertEventsEqual(expectedEvent(1), gotEvents.get(0)); + assertEventsEqual(expectedEvent(2), gotEvents.get(1)); + assertEventsEqual(expectedEvent(3), gotEvents.get(2)); + } + + public void testLogEventTriggerThrottling() throws Exception { + when(mService.logEvent(any())).thenReturn(1234L); + + assertFalse(mLog.log(1, FAKE_EV)); + } + + public void testLogEventFails() throws Exception { + when(mService.logEvent(any())).thenReturn(-1L); // Error. + + assertFalse(mLog.log(1, FAKE_EV)); + } + + public void testLogEventWhenThrottling() throws Exception { + when(mService.logEvent(any())).thenReturn(Long.MAX_VALUE); // Throttled + + // No events are logged. The service is only called once + // After that, throttling state is maintained locally. + assertFalse(mLog.log(1, FAKE_EV)); + assertFalse(mLog.log(2, FAKE_EV)); + + List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1); + assertEventsEqual(expectedEvent(1), gotEvents.get(0)); + } + + public void testLogEventRecoverFromThrottling() throws Exception { + final long throttleTimeout = System.currentTimeMillis() + 50; + when(mService.logEvent(any())).thenReturn(throttleTimeout, 0L); + + assertFalse(mLog.log(1, FAKE_EV)); + new Thread() { + public void run() { + busySpinLog(); + } + }.start(); + + List<ConnectivityMetricsEvent> gotEvents = verifyEvents(2, 200); + assertEventsEqual(expectedEvent(1), gotEvents.get(0)); + assertEventsEqual(expectedEvent(2), gotEvents.get(1)); + } + + public void testLogEventRecoverFromThrottlingWithMultipleCallers() throws Exception { + final long throttleTimeout = System.currentTimeMillis() + 50; + when(mService.logEvent(any())).thenReturn(throttleTimeout, 0L); + + assertFalse(mLog.log(1, FAKE_EV)); + final int nCallers = 10; + for (int i = 0; i < nCallers; i++) { + new Thread() { + public void run() { + busySpinLog(); + } + }.start(); + } + + List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1 + nCallers, 200); + assertEventsEqual(expectedEvent(1), gotEvents.get(0)); + for (int i = 0; i < nCallers; i++) { + assertEventsEqual(expectedEvent(2), gotEvents.get(1 + i)); + } + } + + void busySpinLog() { + final long timeout = 200; + final long stop = System.currentTimeMillis() + timeout; + try { + while (System.currentTimeMillis() < stop) { + if (mLog.log(2, FAKE_EV)) { + return; + } + Thread.sleep(10); + } + } catch (InterruptedException e) { } + } + + List<ConnectivityMetricsEvent> verifyEvents(int n) throws Exception { + verify(mService, times(n)).logEvent(evCaptor.capture()); + return evCaptor.getAllValues(); + } + + List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception { + verify(mService, timeout(timeoutMs).times(n)).logEvent(evCaptor.capture()); + return evCaptor.getAllValues(); + } + + static ConnectivityMetricsEvent expectedEvent(int timestamp) { + return new ConnectivityMetricsEvent((long)timestamp, 0, 0, FAKE_EV); + } + + /** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */ + static void assertEventsEqual(ConnectivityMetricsEvent expected, ConnectivityMetricsEvent got) { + assertEquals(expected.timestamp, got.timestamp); + assertEquals(expected.componentTag, got.componentTag); + assertEquals(expected.eventTag, got.eventTag); + assertEquals(expected.data, got.data); + } +} diff --git a/services/tests/servicestests/src/com/android/server/connectivity/DnsEventListenerServiceTest.java b/services/tests/servicestests/src/com/android/server/connectivity/DnsEventListenerServiceTest.java new file mode 100644 index 000000000000..033b2c96c8f5 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/connectivity/DnsEventListenerServiceTest.java @@ -0,0 +1,197 @@ +/* + * 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.server.connectivity; + +import android.net.ConnectivityManager.NetworkCallback; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.metrics.DnsEvent; +import android.net.metrics.IDnsEventListener; +import android.net.metrics.IpConnectivityLog; + +import junit.framework.TestCase; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertTrue; + +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.io.FileOutputStream; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.List; +import java.util.OptionalInt; +import java.util.stream.IntStream; + +public class DnsEventListenerServiceTest extends TestCase { + + // TODO: read from DnsEventListenerService after this constant is read from system property + static final int BATCH_SIZE = 100; + static final int EVENT_TYPE = IDnsEventListener.EVENT_GETADDRINFO; + // TODO: read from IDnsEventListener + static final int RETURN_CODE = 1; + + static final byte[] EVENT_TYPES = new byte[BATCH_SIZE]; + static final byte[] RETURN_CODES = new byte[BATCH_SIZE]; + static final int[] LATENCIES = new int[BATCH_SIZE]; + static { + for (int i = 0; i < BATCH_SIZE; i++) { + EVENT_TYPES[i] = EVENT_TYPE; + RETURN_CODES[i] = RETURN_CODE; + LATENCIES[i] = i; + } + } + + DnsEventListenerService mDnsService; + + @Mock ConnectivityManager mCm; + @Mock IpConnectivityLog mLog; + ArgumentCaptor<NetworkCallback> mCallbackCaptor; + ArgumentCaptor<DnsEvent> mEvCaptor; + + public void setUp() { + MockitoAnnotations.initMocks(this); + mCallbackCaptor = ArgumentCaptor.forClass(NetworkCallback.class); + mEvCaptor = ArgumentCaptor.forClass(DnsEvent.class); + mDnsService = new DnsEventListenerService(mCm, mLog); + + verify(mCm, times(1)).registerNetworkCallback(any(), mCallbackCaptor.capture()); + } + + public void testOneBatch() throws Exception { + log(105, LATENCIES); + log(106, Arrays.copyOf(LATENCIES, BATCH_SIZE - 1)); // one lookup short of a batch event + + verifyLoggedEvents(new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES)); + + log(106, Arrays.copyOfRange(LATENCIES, BATCH_SIZE - 1, BATCH_SIZE)); + + mEvCaptor = ArgumentCaptor.forClass(DnsEvent.class); // reset argument captor + verifyLoggedEvents( + new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES), + new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES)); + } + + public void testSeveralBatches() throws Exception { + log(105, LATENCIES); + log(106, LATENCIES); + log(105, LATENCIES); + log(107, LATENCIES); + + verifyLoggedEvents( + new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES), + new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES), + new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES), + new DnsEvent(107, EVENT_TYPES, RETURN_CODES, LATENCIES)); + } + + public void testBatchAndNetworkLost() throws Exception { + byte[] eventTypes = Arrays.copyOf(EVENT_TYPES, 20); + byte[] returnCodes = Arrays.copyOf(RETURN_CODES, 20); + int[] latencies = Arrays.copyOf(LATENCIES, 20); + + log(105, LATENCIES); + log(105, latencies); + mCallbackCaptor.getValue().onLost(new Network(105)); + log(105, LATENCIES); + + verifyLoggedEvents( + new DnsEvent(105, eventTypes, returnCodes, latencies), + new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES), + new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES)); + } + + public void testConcurrentBatchesAndDumps() throws Exception { + final long stop = System.currentTimeMillis() + 100; + final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null")); + new Thread() { + public void run() { + while (System.currentTimeMillis() < stop) { + mDnsService.dump(pw); + } + } + }.start(); + + logAsync(105, LATENCIES); + logAsync(106, LATENCIES); + logAsync(107, LATENCIES); + + verifyLoggedEvents(500, + new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES), + new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES), + new DnsEvent(107, EVENT_TYPES, RETURN_CODES, LATENCIES)); + } + + public void testConcurrentBatchesAndNetworkLoss() throws Exception { + logAsync(105, LATENCIES); + Thread.sleep(10L); + // call onLost() asynchronously to logAsync's onDnsEvent() calls. + mCallbackCaptor.getValue().onLost(new Network(105)); + + // do not verify unpredictable batch + verify(mLog, timeout(500).times(1)).log(any()); + } + + void log(int netId, int[] latencies) { + for (int l : latencies) { + mDnsService.onDnsEvent(netId, EVENT_TYPE, RETURN_CODE, l); + } + } + + void logAsync(int netId, int[] latencies) { + new Thread() { + public void run() { + log(netId, latencies); + } + }.start(); + } + + void verifyLoggedEvents(DnsEvent... expected) { + verifyLoggedEvents(0, expected); + } + + void verifyLoggedEvents(int wait, DnsEvent... expectedEvents) { + verify(mLog, timeout(wait).times(expectedEvents.length)).log(mEvCaptor.capture()); + for (DnsEvent got : mEvCaptor.getAllValues()) { + OptionalInt index = IntStream.range(0, expectedEvents.length) + .filter(i -> eventsEqual(expectedEvents[i], got)) + .findFirst(); + // Don't match same expected event more than once. + index.ifPresent(i -> expectedEvents[i] = null); + assertTrue(index.isPresent()); + } + } + + /** equality function for DnsEvent to avoid overriding equals() and hashCode(). */ + static boolean eventsEqual(DnsEvent expected, DnsEvent got) { + return (expected == got) || ((expected != null) && (got != null) + && (expected.netId == got.netId) + && Arrays.equals(expected.eventTypes, got.eventTypes) + && Arrays.equals(expected.returnCodes, got.returnCodes) + && Arrays.equals(expected.latenciesMs, got.latenciesMs)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/connectivity/MetricsLoggerServiceTest.java b/services/tests/servicestests/src/com/android/server/connectivity/MetricsLoggerServiceTest.java new file mode 100644 index 000000000000..5f84ea1bfd96 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/connectivity/MetricsLoggerServiceTest.java @@ -0,0 +1,181 @@ +/* + * 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.server.connectivity; + +import android.content.Context; +import android.net.ConnectivityMetricsEvent; +import android.os.Bundle; +import android.os.RemoteException; +import static android.net.ConnectivityMetricsEvent.Reference; + +import junit.framework.TestCase; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertArrayEquals; + +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.util.Arrays; +import java.util.Comparator; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/* + * TODO: + * - allow overriding MetricsLoggerService constants in tests. + * - test intents are correctly sent after the notification threshold. + * - test oldest events are correctly pushed out when internal deque is full. + * - test throttling triggers correctly. + */ +public class MetricsLoggerServiceTest extends TestCase { + + static final int COMPONENT_TAG = 1; + static final long N_EVENTS = 10L; + static final ConnectivityMetricsEvent EVENTS[] = new ConnectivityMetricsEvent[(int)N_EVENTS]; + static { + for (int i = 0; i < N_EVENTS; i++) { + EVENTS[i] = new ConnectivityMetricsEvent(i, COMPONENT_TAG, i, new Bundle()); + } + } + + static final ConnectivityMetricsEvent NO_EVENTS[] = new ConnectivityMetricsEvent[0]; + + @Mock Context mContext; + MetricsLoggerService mService; + + public void setUp() { + MockitoAnnotations.initMocks(this); + mService = new MetricsLoggerService(mContext); + mService.onStart(); + } + + public void testGetNoEvents() throws Exception { + Reference r = new Reference(0); + assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r)); + assertEquals(0, r.getValue()); + } + + public void testLogAndGetEvents() throws Exception { + mService.mBinder.logEvents(EVENTS); + + Reference r = new Reference(0); + + assertArrayEquals(EVENTS, mService.mBinder.getEvents(r)); + assertEquals(N_EVENTS, r.getValue()); + + assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r)); + assertEquals(N_EVENTS, r.getValue()); + } + + public void testLogOneByOne() throws Exception { + for (ConnectivityMetricsEvent ev : EVENTS) { + mService.mBinder.logEvent(ev); + } + + Reference r = new Reference(0); + + assertArrayEquals(EVENTS, mService.mBinder.getEvents(r)); + assertEquals(N_EVENTS, r.getValue()); + + assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r)); + assertEquals(N_EVENTS, r.getValue()); + } + + public void testInterleavedLogAndGet() throws Exception { + mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 0, 3)); + + Reference r = new Reference(0); + + assertArrayEquals(Arrays.copyOfRange(EVENTS, 0, 3), mService.mBinder.getEvents(r)); + assertEquals(3, r.getValue()); + + mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 3, 8)); + mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 8, 10)); + + assertArrayEquals(Arrays.copyOfRange(EVENTS, 3, 10), mService.mBinder.getEvents(r)); + assertEquals(N_EVENTS, r.getValue()); + + assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r)); + assertEquals(N_EVENTS, r.getValue()); + } + + public void testMultipleGetAll() throws Exception { + mService.mBinder.logEvents(Arrays.copyOf(EVENTS, 3)); + + Reference r1 = new Reference(0); + assertArrayEquals(Arrays.copyOf(EVENTS, 3), mService.mBinder.getEvents(r1)); + assertEquals(3, r1.getValue()); + + mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 3, 10)); + + Reference r2 = new Reference(0); + assertArrayEquals(EVENTS, mService.mBinder.getEvents(r2)); + assertEquals(N_EVENTS, r2.getValue()); + } + + public void testLogAndDumpConcurrently() throws Exception { + for (int i = 0; i < 50; i++) { + mContext = null; + mService = null; + setUp(); + logAndDumpConcurrently(); + } + } + + public void logAndDumpConcurrently() throws Exception { + final CountDownLatch latch = new CountDownLatch((int)N_EVENTS); + final FileDescriptor fd = new FileOutputStream("/dev/null").getFD(); + + for (ConnectivityMetricsEvent ev : EVENTS) { + new Thread() { + public void run() { + mService.mBinder.logEvent(ev); + latch.countDown(); + } + }.start(); + } + + new Thread() { + public void run() { + while (latch.getCount() > 0) { + mService.mBinder.dump(fd, new String[]{"--all"}); + } + } + }.start(); + + latch.await(100, TimeUnit.MILLISECONDS); + + Reference r = new Reference(0); + ConnectivityMetricsEvent[] got = mService.mBinder.getEvents(r); + Arrays.sort(got, new EventComparator()); + assertArrayEquals(EVENTS, got); + assertEquals(N_EVENTS, r.getValue()); + } + + static class EventComparator implements Comparator<ConnectivityMetricsEvent> { + public int compare(ConnectivityMetricsEvent ev1, ConnectivityMetricsEvent ev2) { + return Long.compare(ev1.timestamp, ev2.timestamp); + } + public boolean equal(Object o) { + return o instanceof EventComparator; + } + }; +} |