Merge "Carrier confirmation code."
diff --git a/Android.mk b/Android.mk
index a7cb362..c4f222e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -398,6 +398,7 @@
core/java/com/android/internal/backup/IObbBackupService.aidl \
core/java/com/android/internal/car/ICarServiceHelper.aidl \
core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl \
+ core/java/com/android/internal/net/INetworkWatchlistManager.aidl \
core/java/com/android/internal/policy/IKeyguardDrawnCallback.aidl \
core/java/com/android/internal/policy/IKeyguardDismissCallback.aidl \
core/java/com/android/internal/policy/IKeyguardExitCallback.aidl \
diff --git a/api/current.txt b/api/current.txt
index 97b065e..823cc3f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -31960,6 +31960,7 @@
field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
field public static final java.lang.String DISALLOW_CONFIG_DATE_TIME = "no_config_date_time";
+ field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale";
field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
diff --git a/api/system-current.txt b/api/system-current.txt
index 61aaf18..76a12c5 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -34812,6 +34812,7 @@
field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
field public static final java.lang.String DISALLOW_CONFIG_DATE_TIME = "no_config_date_time";
+ field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale";
field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
diff --git a/api/test-current.txt b/api/test-current.txt
index 2a2967e..0cb6055 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -32229,6 +32229,7 @@
field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
field public static final java.lang.String DISALLOW_CONFIG_DATE_TIME = "no_config_date_time";
+ field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale";
field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
diff --git a/config/compiled-classes-phone b/config/compiled-classes-phone
index bc344d7..fb201ef 100644
--- a/config/compiled-classes-phone
+++ b/config/compiled-classes-phone
@@ -5205,6 +5205,8 @@
com.android.internal.app.AlertController$ButtonHandler
com.android.internal.app.AlertController$RecycleListView
com.android.internal.app.AssistUtils
+com.android.internal.app.ColorDisplayController
+com.android.internal.app.ColorDisplayController$Callback
com.android.internal.app.IAppOpsCallback
com.android.internal.app.IAppOpsCallback$Stub
com.android.internal.app.IAppOpsCallback$Stub$Proxy
@@ -5230,8 +5232,6 @@
com.android.internal.app.IVoiceInteractionSessionShowCallback$Stub
com.android.internal.app.IVoiceInteractor
com.android.internal.app.IVoiceInteractor$Stub
-com.android.internal.app.NightDisplayController
-com.android.internal.app.NightDisplayController$Callback
com.android.internal.app.ProcessMap
com.android.internal.app.ResolverActivity
com.android.internal.app.ToolbarActionBar
diff --git a/config/preloaded-classes b/config/preloaded-classes
index a9e90de..784c3f8 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -2768,6 +2768,7 @@
com.android.ims.ImsException
com.android.internal.R$styleable
com.android.internal.app.AlertController$AlertParams
+com.android.internal.app.ColorDisplayController
com.android.internal.app.IAppOpsCallback
com.android.internal.app.IAppOpsCallback$Stub
com.android.internal.app.IAppOpsService
@@ -2780,7 +2781,6 @@
com.android.internal.app.IVoiceInteractionManagerService$Stub
com.android.internal.app.IVoiceInteractor
com.android.internal.app.IVoiceInteractor$Stub
-com.android.internal.app.NightDisplayController
com.android.internal.appwidget.IAppWidgetService
com.android.internal.appwidget.IAppWidgetService$Stub
com.android.internal.appwidget.IAppWidgetService$Stub$Proxy
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 6989db6..4efc2c7 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -81,6 +81,7 @@
import android.net.IpSecManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkScoreManager;
+import android.net.NetworkWatchlistManager;
import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
import android.net.nsd.INsdManager;
@@ -150,6 +151,7 @@
import com.android.internal.app.IBatteryStats;
import com.android.internal.app.ISoundTriggerService;
import com.android.internal.appwidget.IAppWidgetService;
+import com.android.internal.net.INetworkWatchlistManager;
import com.android.internal.os.IDropBoxManagerService;
import com.android.internal.policy.PhoneLayoutInflater;
@@ -862,6 +864,17 @@
return new ShortcutManager(ctx, IShortcutService.Stub.asInterface(b));
}});
+ registerService(Context.NETWORK_WATCHLIST_SERVICE, NetworkWatchlistManager.class,
+ new CachedServiceFetcher<NetworkWatchlistManager>() {
+ @Override
+ public NetworkWatchlistManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b =
+ ServiceManager.getServiceOrThrow(Context.NETWORK_WATCHLIST_SERVICE);
+ return new NetworkWatchlistManager(ctx,
+ INetworkWatchlistManager.Stub.asInterface(b));
+ }});
+
registerService(Context.SYSTEM_HEALTH_SERVICE, SystemHealthManager.class,
new CachedServiceFetcher<SystemHealthManager>() {
@Override
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index de27b4f..2c1fad1 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -500,15 +500,12 @@
* @hide
*/
public boolean supportSplitScreenWindowingMode() {
- return supportSplitScreenWindowingMode(mWindowingMode, mActivityType);
+ return supportSplitScreenWindowingMode(mActivityType);
}
/** @hide */
- public static boolean supportSplitScreenWindowingMode(int windowingMode, int activityType) {
- if (activityType == ACTIVITY_TYPE_ASSISTANT) {
- return false;
- }
- return windowingMode != WINDOWING_MODE_FREEFORM && windowingMode != WINDOWING_MODE_PINNED;
+ public static boolean supportSplitScreenWindowingMode(int activityType) {
+ return activityType != ACTIVITY_TYPE_ASSISTANT;
}
/** @hide */
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index dbaace2..29e7439 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -118,7 +118,15 @@
AppIdleStateChangeListener listener);
public static abstract class AppIdleStateChangeListener {
- public abstract void onAppIdleStateChanged(String packageName, int userId, boolean idle);
+
+ /** Callback to inform listeners that the idle state has changed to a new bucket. */
+ public abstract void onAppIdleStateChanged(String packageName, int userId, boolean idle,
+ int bucket);
+
+ /**
+ * Callback to inform listeners that the parole state has changed. This means apps are
+ * allowed to do work even if they're idle or in a low bucket.
+ */
public abstract void onParoleStateChanged(boolean isParoleOn);
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c165fb3..01ad3ad 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3413,6 +3413,8 @@
public static final String NETWORK_STATS_SERVICE = "netstats";
/** {@hide} */
public static final String NETWORK_POLICY_SERVICE = "netpolicy";
+ /** {@hide} */
+ public static final String NETWORK_WATCHLIST_SERVICE = "network_watchlist";
/**
* Use with {@link #getSystemService} to retrieve a {@link
diff --git a/core/java/android/net/IIpConnectivityMetrics.aidl b/core/java/android/net/IIpConnectivityMetrics.aidl
index 6f07b31..aeaf09d 100644
--- a/core/java/android/net/IIpConnectivityMetrics.aidl
+++ b/core/java/android/net/IIpConnectivityMetrics.aidl
@@ -30,11 +30,11 @@
int logEvent(in ConnectivityMetricsEvent event);
/**
- * At most one callback can be registered (by DevicePolicyManager).
+ * Callback can be registered by DevicePolicyManager or NetworkWatchlistService only.
* @return status {@code true} if registering/unregistering of the callback was successful,
* {@code false} otherwise (might happen if IIpConnectivityMetrics is not available,
* if it happens make sure you call it when the service is up in the caller)
*/
- boolean registerNetdEventCallback(in INetdEventCallback callback);
- boolean unregisterNetdEventCallback();
+ boolean addNetdEventCallback(in int callerType, in INetdEventCallback callback);
+ boolean removeNetdEventCallback(in int callerType);
}
diff --git a/core/java/android/net/INetdEventCallback.aidl b/core/java/android/net/INetdEventCallback.aidl
index 49436be..1fd9423 100644
--- a/core/java/android/net/INetdEventCallback.aidl
+++ b/core/java/android/net/INetdEventCallback.aidl
@@ -19,6 +19,10 @@
/** {@hide} */
oneway interface INetdEventCallback {
+ // Possible addNetdEventCallback callers.
+ const int CALLBACK_CALLER_DEVICE_POLICY = 0;
+ const int CALLBACK_CALLER_NETWORK_WATCHLIST = 1;
+
/**
* Reports a single DNS lookup function call.
* This method must not block or perform long-running operations.
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index e76d17d..f6a69ba 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -16,29 +16,128 @@
package android.net;
-import com.android.internal.annotations.VisibleForTesting;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.BitUtils;
import java.util.Arrays;
+import java.util.Random;
+import java.util.StringJoiner;
/**
+ * Represents a mac address.
+ *
* @hide
*/
-public final class MacAddress {
-
- // TODO: add isLocallyAssigned().
- // TODO: add getRandomAddress() factory method.
+public final class MacAddress implements Parcelable {
private static final int ETHER_ADDR_LEN = 6;
- private static final byte FF = (byte) 0xff;
- @VisibleForTesting
- static final byte[] ETHER_ADDR_BROADCAST = { FF, FF, FF, FF, FF, FF };
+ private static final byte[] ETHER_ADDR_BROADCAST = addr(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
+ /** The broadcast mac address. */
+ public static final MacAddress BROADCAST_ADDRESS = new MacAddress(ETHER_ADDR_BROADCAST);
+
+ /** The zero mac address. */
+ public static final MacAddress ALL_ZEROS_ADDRESS = new MacAddress(0);
+
+ /** Represents categories of mac addresses. */
public enum MacAddressType {
UNICAST,
MULTICAST,
BROADCAST;
}
+ private static final long VALID_LONG_MASK = BROADCAST_ADDRESS.mAddr;
+ private static final long LOCALLY_ASSIGNED_MASK = new MacAddress("2:0:0:0:0:0").mAddr;
+ private static final long MULTICAST_MASK = new MacAddress("1:0:0:0:0:0").mAddr;
+ private static final long OUI_MASK = new MacAddress("ff:ff:ff:0:0:0").mAddr;
+ private static final long NIC_MASK = new MacAddress("0:0:0:ff:ff:ff").mAddr;
+ private static final MacAddress BASE_ANDROID_MAC = new MacAddress("da:a1:19:0:0:0");
+
+ // Internal representation of the mac address as a single 8 byte long.
+ // The encoding scheme sets the two most significant bytes to 0. The 6 bytes of the
+ // mac address are encoded in the 6 least significant bytes of the long, where the first
+ // byte of the array is mapped to the 3rd highest logical byte of the long, the second
+ // byte of the array is mapped to the 4th highest logical byte of the long, and so on.
+ private final long mAddr;
+
+ private MacAddress(long addr) {
+ mAddr = addr;
+ }
+
+ /** Creates a MacAddress for the given byte representation. */
+ public MacAddress(byte[] addr) {
+ this(longAddrFromByteAddr(addr));
+ }
+
+ /** Creates a MacAddress for the given string representation. */
+ public MacAddress(String addr) {
+ this(longAddrFromByteAddr(byteAddrFromStringAddr(addr)));
+ }
+
+ /** Returns the MacAddressType of this MacAddress. */
+ public MacAddressType addressType() {
+ if (equals(BROADCAST_ADDRESS)) {
+ return MacAddressType.BROADCAST;
+ }
+ if (isMulticastAddress()) {
+ return MacAddressType.MULTICAST;
+ }
+ return MacAddressType.UNICAST;
+ }
+
+ /** Returns true if this MacAddress corresponds to a multicast address. */
+ public boolean isMulticastAddress() {
+ return (mAddr & MULTICAST_MASK) != 0;
+ }
+
+ /** Returns true if this MacAddress corresponds to a locally assigned address. */
+ public boolean isLocallyAssigned() {
+ return (mAddr & LOCALLY_ASSIGNED_MASK) != 0;
+ }
+
+ /** Returns a byte array representation of this MacAddress. */
+ public byte[] toByteArray() {
+ return byteAddrFromLongAddr(mAddr);
+ }
+
+ @Override
+ public String toString() {
+ return stringAddrFromByteAddr(byteAddrFromLongAddr(mAddr));
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) ((mAddr >> 32) ^ mAddr);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return (o instanceof MacAddress) && ((MacAddress) o).mAddr == mAddr;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(mAddr);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<MacAddress> CREATOR =
+ new Parcelable.Creator<MacAddress>() {
+ public MacAddress createFromParcel(Parcel in) {
+ return new MacAddress(in.readLong());
+ }
+
+ public MacAddress[] newArray(int size) {
+ return new MacAddress[size];
+ }
+ };
+
/** Return true if the given byte array is not null and has the length of a mac address. */
public static boolean isMacAddress(byte[] addr) {
return addr != null && addr.length == ETHER_ADDR_LEN;
@@ -46,17 +145,130 @@
/**
* Return the MacAddressType of the mac address represented by the given byte array,
- * or null if the given byte array does not represent an mac address. */
+ * or null if the given byte array does not represent an mac address.
+ */
public static MacAddressType macAddressType(byte[] addr) {
if (!isMacAddress(addr)) {
return null;
}
- if (Arrays.equals(addr, ETHER_ADDR_BROADCAST)) {
- return MacAddressType.BROADCAST;
+ return new MacAddress(addr).addressType();
+ }
+
+ /** DOCME */
+ public static byte[] byteAddrFromStringAddr(String addr) {
+ if (addr == null) {
+ throw new IllegalArgumentException("cannot convert the null String");
}
- if ((addr[0] & 0x01) == 1) {
- return MacAddressType.MULTICAST;
+ String[] parts = addr.split(":");
+ if (parts.length != ETHER_ADDR_LEN) {
+ throw new IllegalArgumentException(addr + " was not a valid MAC address");
}
- return MacAddressType.UNICAST;
+ byte[] bytes = new byte[ETHER_ADDR_LEN];
+ for (int i = 0; i < ETHER_ADDR_LEN; i++) {
+ int x = Integer.valueOf(parts[i], 16);
+ if (x < 0 || 0xff < x) {
+ throw new IllegalArgumentException(addr + "was not a valid MAC address");
+ }
+ bytes[i] = (byte) x;
+ }
+ return bytes;
+ }
+
+ /** DOCME */
+ public static String stringAddrFromByteAddr(byte[] addr) {
+ if (!isMacAddress(addr)) {
+ return null;
+ }
+ StringJoiner j = new StringJoiner(":");
+ for (byte b : addr) {
+ j.add(Integer.toHexString(BitUtils.uint8(b)));
+ }
+ return j.toString();
+ }
+
+ /** @hide */
+ public static byte[] byteAddrFromLongAddr(long addr) {
+ byte[] bytes = new byte[ETHER_ADDR_LEN];
+ int index = ETHER_ADDR_LEN;
+ while (index-- > 0) {
+ bytes[index] = (byte) addr;
+ addr = addr >> 8;
+ }
+ return bytes;
+ }
+
+ /** @hide */
+ public static long longAddrFromByteAddr(byte[] addr) {
+ if (!isMacAddress(addr)) {
+ throw new IllegalArgumentException(
+ Arrays.toString(addr) + " was not a valid MAC address");
+ }
+ long longAddr = 0;
+ for (byte b : addr) {
+ longAddr = (longAddr << 8) + BitUtils.uint8(b);
+ }
+ return longAddr;
+ }
+
+ /** @hide */
+ public static long longAddrFromStringAddr(String addr) {
+ if (addr == null) {
+ throw new IllegalArgumentException("cannot convert the null String");
+ }
+ String[] parts = addr.split(":");
+ if (parts.length != ETHER_ADDR_LEN) {
+ throw new IllegalArgumentException(addr + " was not a valid MAC address");
+ }
+ long longAddr = 0;
+ int index = ETHER_ADDR_LEN;
+ while (index-- > 0) {
+ int x = Integer.valueOf(parts[index], 16);
+ if (x < 0 || 0xff < x) {
+ throw new IllegalArgumentException(addr + "was not a valid MAC address");
+ }
+ longAddr = x + (longAddr << 8);
+ }
+ return longAddr;
+ }
+
+ /** @hide */
+ public static String stringAddrFromLongAddr(long addr) {
+ addr = Long.reverseBytes(addr) >> 16;
+ StringJoiner j = new StringJoiner(":");
+ for (int i = 0; i < ETHER_ADDR_LEN; i++) {
+ j.add(Integer.toHexString((byte) addr));
+ addr = addr >> 8;
+ }
+ return j.toString();
+ }
+
+ /**
+ * Returns a randomely generated mac address with the Android OUI value "DA-A1-19".
+ * The locally assigned bit is always set to 1.
+ */
+ public static MacAddress getRandomAddress() {
+ return getRandomAddress(BASE_ANDROID_MAC, new Random());
+ }
+
+ /**
+ * Returns a randomely generated mac address using the given Random object and the same
+ * OUI values as the given MacAddress. The locally assigned bit is always set to 1.
+ */
+ public static MacAddress getRandomAddress(MacAddress base, Random r) {
+ long longAddr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong()) | LOCALLY_ASSIGNED_MASK;
+ return new MacAddress(longAddr);
+ }
+
+ // Convenience function for working around the lack of byte literals.
+ private static byte[] addr(int... in) {
+ if (in.length != ETHER_ADDR_LEN) {
+ throw new IllegalArgumentException(Arrays.toString(in)
+ + " was not an array with length equal to " + ETHER_ADDR_LEN);
+ }
+ byte[] out = new byte[ETHER_ADDR_LEN];
+ for (int i = 0; i < ETHER_ADDR_LEN; i++) {
+ out[i] = (byte) in[i];
+ }
+ return out;
}
}
diff --git a/core/java/android/net/NetworkWatchlistManager.java b/core/java/android/net/NetworkWatchlistManager.java
new file mode 100644
index 0000000..42e43c8
--- /dev/null
+++ b/core/java/android/net/NetworkWatchlistManager.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.internal.net.INetworkWatchlistManager;
+import com.android.internal.util.Preconditions;
+
+/**
+ * Class that manage network watchlist in system.
+ * @hide
+ */
+@SystemService(Context.NETWORK_WATCHLIST_SERVICE)
+public class NetworkWatchlistManager {
+
+ private static final String TAG = "NetworkWatchlistManager";
+ private static final String SHARED_MEMORY_TAG = "NETWORK_WATCHLIST_SHARED_MEMORY";
+
+ private final Context mContext;
+ private final INetworkWatchlistManager mNetworkWatchlistManager;
+
+ /**
+ * @hide
+ */
+ public NetworkWatchlistManager(Context context, INetworkWatchlistManager manager) {
+ mContext = context;
+ mNetworkWatchlistManager = manager;
+ }
+
+ /**
+ * @hide
+ */
+ public NetworkWatchlistManager(Context context) {
+ mContext = Preconditions.checkNotNull(context, "missing context");
+ mNetworkWatchlistManager = (INetworkWatchlistManager)
+ INetworkWatchlistManager.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORK_WATCHLIST_SERVICE));
+ }
+
+ /**
+ * Report network watchlist records if necessary.
+ *
+ * Watchlist report process will run summarize records into a single report, then the
+ * report will be processed by differential privacy framework and store it on disk.
+ *
+ * @hide
+ */
+ public void reportWatchlistIfNecessary() {
+ try {
+ mNetworkWatchlistManager.reportWatchlistIfNecessary();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Cannot report records", e);
+ e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 0b1569c..4817813 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -63,7 +63,12 @@
* This implementation does check the server's certificate hostname, but only
* for createSocket variants that specify a hostname. When using methods that
* use {@link InetAddress} or which return an unconnected socket, you MUST
- * verify the server's identity yourself to ensure a secure connection.</p>
+ * verify the server's identity yourself to ensure a secure connection.
+ *
+ * Refer to
+ * <a href="https://developer.android.com/training/articles/security-gms-provider.html">
+ * Updating Your Security Provider to Protect Against SSL Exploits</a>
+ * for further information.</p>
*
* <p>One way to verify the server's identity is to use
* {@link HttpsURLConnection#getDefaultHostnameVerifier()} to get a
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 01b6535..dd4825e 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -526,7 +526,11 @@
ServiceType.SCREEN_BRIGHTNESS,
ServiceType.SOUND,
ServiceType.BATTERY_STATS,
- ServiceType.DATA_SAVER})
+ ServiceType.DATA_SAVER,
+ ServiceType.FORCE_ALL_APPS_STANDBY_JOBS,
+ ServiceType.FORCE_ALL_APPS_STANDBY_ALARMS,
+ ServiceType.OPTIONAL_SENSORS,
+ })
public @interface ServiceType {
int NULL = 0;
int GPS = 1;
@@ -539,6 +543,21 @@
int SOUND = 8;
int BATTERY_STATS = 9;
int DATA_SAVER = 10;
+
+ /**
+ * Whether the job scheduler should force app standby on all apps on battery saver or not.
+ */
+ int FORCE_ALL_APPS_STANDBY_JOBS = 11;
+
+ /**
+ * Whether the alarm manager should force app standby on all apps on battery saver or not.
+ */
+ int FORCE_ALL_APPS_STANDBY_ALARMS = 12;
+
+ /**
+ * Whether to disable non-essential sensors. (e.g. edge sensors.)
+ */
+ int OPTIONAL_SENSORS = 13;
}
final Context mContext;
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index a01b8ed..77ac2651 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -18,6 +18,8 @@
import android.view.Display;
+import java.util.function.Consumer;
+
/**
* Power manager local system service interface.
*
@@ -125,6 +127,23 @@
public abstract void registerLowPowerModeObserver(LowPowerModeListener listener);
+ /**
+ * Same as {@link #registerLowPowerModeObserver} but can take a lambda.
+ */
+ public void registerLowPowerModeObserver(int serviceType, Consumer<PowerSaveState> listener) {
+ registerLowPowerModeObserver(new LowPowerModeListener() {
+ @Override
+ public int getServiceType() {
+ return serviceType;
+ }
+
+ @Override
+ public void onLowPowerModeChanged(PowerSaveState state) {
+ listener.accept(state);
+ }
+ });
+ }
+
public interface LowPowerModeListener {
int getServiceType();
void onLowPowerModeChanged(PowerSaveState state);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 28836e4..de52736 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -140,6 +140,18 @@
public static final String DISALLOW_CONFIG_WIFI = "no_config_wifi";
/**
+ * Specifies if a user is disallowed from changing the device
+ * language. The default value is <code>false</code>.
+ *
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_CONFIG_LOCALE = "no_config_locale";
+
+ /**
* Specifies if a user is disallowed from installing applications.
* The default value is <code>false</code>.
*
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1716305..1bef2b3 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8522,6 +8522,13 @@
public static final String NETWORK_METERED_MULTIPATH_PREFERENCE =
"network_metered_multipath_preference";
+ /**
+ * Network watchlist last report time.
+ * @hide
+ */
+ public static final String NETWORK_WATCHLIST_LAST_REPORT_TIME =
+ "network_watchlist_last_report_time";
+
/**
* The thresholds of the wifi throughput badging (SD, HD etc.) as a comma-delimited list of
* colon-delimited key-value pairs. The key is the badging enum value defined in
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index b07e94a..7412ef4 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -20,6 +20,7 @@
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
@@ -66,7 +67,7 @@
*/
static ApkVerityResult generateApkVerity(RandomAccessFile apk,
SignatureInfo signatureInfo, ByteBufferFactory bufferFactory)
- throws IOException, SecurityException, NoSuchAlgorithmException {
+ throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
assertSigningBlockAlignedAndHasFullPages(signatureInfo);
long signingBlockSize =
@@ -112,6 +113,7 @@
private final ByteBuffer mOutput;
private final MessageDigest mMd;
+ private final byte[] mDigestBuffer = new byte[DIGEST_SIZE_BYTES];
private final byte[] mSalt;
private BufferedDigester(byte[] salt, ByteBuffer output) throws NoSuchAlgorithmException {
@@ -129,21 +131,22 @@
* consumption will continuous from there.
*/
@Override
- public void consume(ByteBuffer buffer) {
+ public void consume(ByteBuffer buffer) throws DigestException {
int offset = buffer.position();
int remaining = buffer.remaining();
while (remaining > 0) {
int allowance = (int) Math.min(remaining, BUFFER_SIZE - mBytesDigestedSinceReset);
- mMd.update(slice(buffer, offset, offset + allowance));
+ // Optimization: set the buffer limit to avoid allocating a new ByteBuffer object.
+ buffer.limit(buffer.position() + allowance);
+ mMd.update(buffer);
offset += allowance;
remaining -= allowance;
mBytesDigestedSinceReset += allowance;
if (mBytesDigestedSinceReset == BUFFER_SIZE) {
- byte[] digest = mMd.digest();
- mOutput.put(digest);
-
- mMd.reset();
+ mMd.digest(mDigestBuffer, 0, mDigestBuffer.length);
+ mOutput.put(mDigestBuffer);
+ // After digest, MessageDigest resets automatically, so no need to reset again.
mMd.update(mSalt);
mBytesDigestedSinceReset = 0;
}
@@ -152,12 +155,12 @@
/** Finish the current digestion if any. */
@Override
- public void finish() {
+ public void finish() throws DigestException {
if (mBytesDigestedSinceReset == 0) {
return;
}
- byte[] digest = mMd.digest();
- mOutput.put(digest);
+ mMd.digest(mDigestBuffer, 0, mDigestBuffer.length);
+ mOutput.put(mDigestBuffer);
}
private void fillUpLastOutputChunk() {
@@ -174,7 +177,7 @@
* digest the remaining.
*/
private static void consumeByChunk(DataDigester digester, DataSource source, int chunkSize)
- throws IOException {
+ throws IOException, DigestException {
long inputRemaining = source.size();
long inputOffset = 0;
while (inputRemaining > 0) {
@@ -191,7 +194,7 @@
private static void generateApkVerityDigestAtLeafLevel(RandomAccessFile apk,
SignatureInfo signatureInfo, byte[] salt, ByteBuffer output)
- throws IOException, NoSuchAlgorithmException {
+ throws IOException, NoSuchAlgorithmException, DigestException {
BufferedDigester digester = new BufferedDigester(salt, output);
// 1. Digest from the beginning of the file, until APK Signing Block is reached.
@@ -230,7 +233,7 @@
private static byte[] generateApkVerityTree(RandomAccessFile apk, SignatureInfo signatureInfo,
byte[] salt, int[] levelOffset, ByteBuffer output)
- throws IOException, NoSuchAlgorithmException {
+ throws IOException, NoSuchAlgorithmException, DigestException {
// 1. Digest the apk to generate the leaf level hashes.
generateApkVerityDigestAtLeafLevel(apk, signatureInfo, salt, slice(output,
levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
diff --git a/core/java/android/util/apk/ByteBufferDataSource.java b/core/java/android/util/apk/ByteBufferDataSource.java
index c2b9eff..3976568 100644
--- a/core/java/android/util/apk/ByteBufferDataSource.java
+++ b/core/java/android/util/apk/ByteBufferDataSource.java
@@ -18,6 +18,7 @@
import java.io.IOException;
import java.nio.ByteBuffer;
+import java.security.DigestException;
/**
* {@link DataSource} which provides data from a {@link ByteBuffer}.
@@ -42,7 +43,7 @@
@Override
public void feedIntoDataDigester(DataDigester md, long offset, int size)
- throws IOException {
+ throws IOException, DigestException {
// There's no way to tell MessageDigest to read data from ByteBuffer from a position
// other than the buffer's current position. We thus need to change the buffer's
// position to match the requested offset.
diff --git a/core/java/android/util/apk/DataDigester.java b/core/java/android/util/apk/DataDigester.java
index 74dce7e..278be80 100644
--- a/core/java/android/util/apk/DataDigester.java
+++ b/core/java/android/util/apk/DataDigester.java
@@ -17,11 +17,12 @@
package android.util.apk;
import java.nio.ByteBuffer;
+import java.security.DigestException;
interface DataDigester {
/** Consumes the {@link ByteBuffer}. */
- void consume(ByteBuffer buffer);
+ void consume(ByteBuffer buffer) throws DigestException;
/** Finishes the digestion. Must be called after the last {@link #consume(ByteBuffer)}. */
- void finish();
+ void finish() throws DigestException;
}
diff --git a/core/java/android/util/apk/DataSource.java b/core/java/android/util/apk/DataSource.java
index 2b39f49..82f3800 100644
--- a/core/java/android/util/apk/DataSource.java
+++ b/core/java/android/util/apk/DataSource.java
@@ -17,6 +17,7 @@
package android.util.apk;
import java.io.IOException;
+import java.security.DigestException;
/** Source of data to be digested. */
interface DataSource {
@@ -32,5 +33,6 @@
* @param offset offset of the region inside this data source.
* @param size size (in bytes) of the region.
*/
- void feedIntoDataDigester(DataDigester md, long offset, int size) throws IOException;
+ void feedIntoDataDigester(DataDigester md, long offset, int size)
+ throws IOException, DigestException;
}
diff --git a/core/java/android/util/apk/MemoryMappedFileDataSource.java b/core/java/android/util/apk/MemoryMappedFileDataSource.java
index 04a1c6b..8d2b1e32 100644
--- a/core/java/android/util/apk/MemoryMappedFileDataSource.java
+++ b/core/java/android/util/apk/MemoryMappedFileDataSource.java
@@ -24,6 +24,7 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.DirectByteBuffer;
+import java.security.DigestException;
/**
* {@link DataSource} which provides data from a file descriptor by memory-mapping the sections
@@ -55,7 +56,7 @@
@Override
public void feedIntoDataDigester(DataDigester md, long offset, int size)
- throws IOException {
+ throws IOException, DigestException {
// IMPLEMENTATION NOTE: After a lot of experimentation, the implementation of this
// method was settled on a straightforward mmap with prefaulting.
//
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index 7e1846a..cd1b190 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ClipData;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
@@ -140,6 +141,30 @@
}
/**
+ * An interface to customize drag and drop behaviors.
+ */
+ public interface IDragDropCallback {
+ /**
+ * Called when drag operation is started.
+ */
+ default boolean performDrag(IWindow window, IBinder dragToken,
+ int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
+ ClipData data) {
+ return true;
+ }
+
+ /**
+ * Called when drop result is reported.
+ */
+ default void reportDropResult(IWindow window, boolean consumed) {}
+
+ /**
+ * Called when drag operation is cancelled.
+ */
+ default void cancelDragAndDrop(IBinder dragToken) {}
+ }
+
+ /**
* Request that the window manager call
* {@link DisplayManagerInternal#performTraversalInTransactionFromWindowManager}
* within a surface transaction at a later time.
@@ -351,4 +376,9 @@
* {@param vr2dDisplayId}.
*/
public abstract void setVr2dDisplayId(int vr2dDisplayId);
+
+ /**
+ * Sets callback to DragDropController.
+ */
+ public abstract void registerDragDropControllerCallback(IDragDropCallback callback);
}
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index e35bb92..aeb8489 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -133,27 +133,6 @@
}
/**
- * Returns a {@link LinksInfo} that may be applied to the text to annotate it with links
- * information.
- *
- * @param text the text to generate annotations for
- * @param linkMask See {@link android.text.util.Linkify} for a list of linkMasks that may be
- * specified. Subclasses of this interface may specify additional linkMasks
- * @param defaultLocales ordered list of locale preferences that can be used to disambiguate
- * the provided text. If no locale preferences exist, set this to null or an empty locale
- * list in which case the classifier will decide whether to use no locale information, use
- * a default locale, or use the system default.
- *
- * @throws IllegalArgumentException if text is null
- * @hide
- */
- @WorkerThread
- default LinksInfo getLinks(
- @NonNull CharSequence text, int linkMask, @Nullable LocaleList defaultLocales) {
- return LinksInfo.NO_OP;
- }
-
- /**
* Returns a {@link TextLinks} that may be applied to the text to annotate it with links
* information.
*
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 9c4fd57..107013a 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -30,13 +30,8 @@
import android.provider.Browser;
import android.provider.ContactsContract;
import android.provider.Settings;
-import android.text.Spannable;
-import android.text.TextUtils;
-import android.text.method.WordIterator;
-import android.text.style.ClickableSpan;
import android.text.util.Linkify;
import android.util.Patterns;
-import android.view.View;
import android.widget.TextViewMetrics;
import com.android.internal.annotations.GuardedBy;
@@ -46,13 +41,8 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.text.BreakIterator;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -194,21 +184,6 @@
}
@Override
- public LinksInfo getLinks(
- @NonNull CharSequence text, int linkMask, @Nullable LocaleList defaultLocales) {
- Preconditions.checkArgument(text != null);
- try {
- return LinksInfoFactory.create(
- mContext, getSmartSelection(defaultLocales), text.toString(), linkMask);
- } catch (Throwable t) {
- // Avoid throwing from this method. Log the error.
- Log.e(LOG_TAG, "Error getting links info.", t);
- }
- // Getting here means something went wrong, return a NO_OP result.
- return TextClassifier.NO_OP.getLinks(text, linkMask, defaultLocales);
- }
-
- @Override
public TextLinks generateLinks(
@NonNull CharSequence text, @Nullable TextLinks.Options options) {
Preconditions.checkNotNull(text);
@@ -516,180 +491,6 @@
}
/**
- * Detects and creates links for specified text.
- */
- private static final class LinksInfoFactory {
-
- private LinksInfoFactory() {}
-
- public static LinksInfo create(
- Context context, SmartSelection smartSelection, String text, int linkMask) {
- final WordIterator wordIterator = new WordIterator();
- wordIterator.setCharSequence(text, 0, text.length());
- final List<SpanSpec> spans = new ArrayList<>();
- int start = 0;
- int end;
- while ((end = wordIterator.nextBoundary(start)) != BreakIterator.DONE) {
- final String token = text.substring(start, end);
- if (TextUtils.isEmpty(token)) {
- continue;
- }
-
- final int[] selection = smartSelection.suggest(text, start, end);
- final int selectionStart = selection[0];
- final int selectionEnd = selection[1];
- if (selectionStart >= 0 && selectionEnd <= text.length()
- && selectionStart <= selectionEnd) {
- final SmartSelection.ClassificationResult[] results =
- smartSelection.classifyText(
- text, selectionStart, selectionEnd,
- getHintFlags(text, selectionStart, selectionEnd));
- if (results.length > 0) {
- final String type = getHighestScoringType(results);
- if (matches(type, linkMask)) {
- // For links without disambiguation, we simply use the default intent.
- final List<Intent> intents = IntentFactory.create(
- context, type, text.substring(selectionStart, selectionEnd));
- if (!intents.isEmpty() && hasActivityHandler(context, intents.get(0))) {
- final ClickableSpan span = createSpan(context, intents.get(0));
- spans.add(new SpanSpec(selectionStart, selectionEnd, span));
- }
- }
- }
- }
- start = end;
- }
- return new LinksInfoImpl(text, avoidOverlaps(spans, text));
- }
-
- /**
- * Returns true if the classification type matches the specified linkMask.
- */
- private static boolean matches(String type, int linkMask) {
- type = type.trim().toLowerCase(Locale.ENGLISH);
- if ((linkMask & Linkify.PHONE_NUMBERS) != 0
- && TextClassifier.TYPE_PHONE.equals(type)) {
- return true;
- }
- if ((linkMask & Linkify.EMAIL_ADDRESSES) != 0
- && TextClassifier.TYPE_EMAIL.equals(type)) {
- return true;
- }
- if ((linkMask & Linkify.MAP_ADDRESSES) != 0
- && TextClassifier.TYPE_ADDRESS.equals(type)) {
- return true;
- }
- if ((linkMask & Linkify.WEB_URLS) != 0
- && TextClassifier.TYPE_URL.equals(type)) {
- return true;
- }
- return false;
- }
-
- /**
- * Trim the number of spans so that no two spans overlap.
- *
- * This algorithm first ensures that there is only one span per start index, then it
- * makes sure that no two spans overlap.
- */
- private static List<SpanSpec> avoidOverlaps(List<SpanSpec> spans, String text) {
- Collections.sort(spans, Comparator.comparingInt(span -> span.mStart));
- // Group spans by start index. Take the longest span.
- final Map<Integer, SpanSpec> reps = new LinkedHashMap<>(); // order matters.
- final int size = spans.size();
- for (int i = 0; i < size; i++) {
- final SpanSpec span = spans.get(i);
- final LinksInfoFactory.SpanSpec rep = reps.get(span.mStart);
- if (rep == null || rep.mEnd < span.mEnd) {
- reps.put(span.mStart, span);
- }
- }
- // Avoid span intersections. Take the longer span.
- final LinkedList<SpanSpec> result = new LinkedList<>();
- for (SpanSpec rep : reps.values()) {
- if (result.isEmpty()) {
- result.add(rep);
- continue;
- }
-
- final SpanSpec last = result.getLast();
- if (rep.mStart < last.mEnd) {
- // Spans intersect. Use the one with characters.
- if ((rep.mEnd - rep.mStart) > (last.mEnd - last.mStart)) {
- result.set(result.size() - 1, rep);
- }
- } else {
- result.add(rep);
- }
- }
- return result;
- }
-
- private static ClickableSpan createSpan(final Context context, final Intent intent) {
- return new ClickableSpan() {
- // TODO: Style this span.
- @Override
- public void onClick(View widget) {
- context.startActivity(intent);
- }
- };
- }
-
- private static boolean hasActivityHandler(Context context, Intent intent) {
- if (intent == null) {
- return false;
- }
- final ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(intent, 0);
- return resolveInfo != null && resolveInfo.activityInfo != null;
- }
-
- /**
- * Implementation of LinksInfo that adds ClickableSpans to the specified text.
- */
- private static final class LinksInfoImpl implements LinksInfo {
-
- private final CharSequence mOriginalText;
- private final List<SpanSpec> mSpans;
-
- LinksInfoImpl(CharSequence originalText, List<SpanSpec> spans) {
- mOriginalText = originalText;
- mSpans = spans;
- }
-
- @Override
- public boolean apply(@NonNull CharSequence text) {
- Preconditions.checkArgument(text != null);
- if (text instanceof Spannable && mOriginalText.toString().equals(text.toString())) {
- Spannable spannable = (Spannable) text;
- final int size = mSpans.size();
- for (int i = 0; i < size; i++) {
- final SpanSpec span = mSpans.get(i);
- spannable.setSpan(span.mSpan, span.mStart, span.mEnd, 0);
- }
- return true;
- }
- return false;
- }
- }
-
- /**
- * Span plus its start and end index.
- */
- private static final class SpanSpec {
-
- private final int mStart;
- private final int mEnd;
- private final ClickableSpan mSpan;
-
- SpanSpec(int start, int end, ClickableSpan span) {
- mStart = start;
- mEnd = end;
- mSpan = span;
- }
- }
- }
-
- /**
* Creates intents based on the classification type.
*/
private static final class IntentFactory {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 9295f5c..665d694 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -308,10 +308,15 @@
* WebView may upload anonymous diagnostic data to Google when the user has consented. This data
* helps Google improve WebView. Data is collected on a per-app basis for each app which has
* instantiated a WebView. An individual app can opt out of this feature by putting the following
- * tag in its manifest:
+ * tag in its manifest's {@code <application>} element:
* <pre>
- * <meta-data android:name="android.webkit.WebView.MetricsOptOut"
- * android:value="true" />
+ * <manifest>
+ * <application>
+ * ...
+ * <meta-data android:name="android.webkit.WebView.MetricsOptOut"
+ * android:value="true" />
+ * </application>
+ * </manifest>
* </pre>
* <p>
* Data will only be uploaded for a given app if the user has consented AND the app has not opted
@@ -323,11 +328,17 @@
* If Safe Browsing is enabled, WebView will block malicious URLs and present a warning UI to the
* user to allow them to navigate back safely or proceed to the malicious page.
* <p>
- * The recommended way for apps to enable the feature is putting the following tag in the manifest:
+ * The recommended way for apps to enable the feature is putting the following tag in the manifest's
+ * {@code <application>} element:
* <p>
* <pre>
- * <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
- * android:value="true" />
+ * <manifest>
+ * <application>
+ * ...
+ * <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
+ * android:value="true" />
+ * </application>
+ * </manifest>
* </pre>
*
*/
diff --git a/core/java/com/android/internal/app/NightDisplayController.java b/core/java/com/android/internal/app/ColorDisplayController.java
similarity index 98%
rename from core/java/com/android/internal/app/NightDisplayController.java
rename to core/java/com/android/internal/app/ColorDisplayController.java
index b8bfc64..b8682a8 100644
--- a/core/java/com/android/internal/app/NightDisplayController.java
+++ b/core/java/com/android/internal/app/ColorDisplayController.java
@@ -42,14 +42,14 @@
import java.time.format.DateTimeParseException;
/**
- * Controller for managing Night display settings.
+ * Controller for managing night display and color mode settings.
* <p/>
* Night display tints your screen red at night. This makes it easier to look at your screen in
* dim light and may help you fall asleep more easily.
*/
-public final class NightDisplayController {
+public final class ColorDisplayController {
- private static final String TAG = "NightDisplayController";
+ private static final String TAG = "ColorDisplayController";
private static final boolean DEBUG = false;
@Retention(RetentionPolicy.SOURCE)
@@ -114,11 +114,11 @@
private Callback mCallback;
- public NightDisplayController(@NonNull Context context) {
+ public ColorDisplayController(@NonNull Context context) {
this(context, ActivityManager.getCurrentUser());
}
- public NightDisplayController(@NonNull Context context, int userId) {
+ public ColorDisplayController(@NonNull Context context, int userId) {
mContext = context.getApplicationContext();
mUserId = userId;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
similarity index 72%
copy from packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl
copy to core/java/com/android/internal/net/INetworkWatchlistManager.aidl
index 8cf3be8..7e88369 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl
+++ b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
@@ -14,10 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.shared.recents.model;
+package com.android.internal.net;
-import android.view.MotionEvent;
+import android.os.SharedMemory;
-oneway interface IOverviewProxy {
- void onMotionEvent(in MotionEvent event);
+/** {@hide} */
+interface INetworkWatchlistManager {
+ boolean startWatchlistLogging();
+ boolean stopWatchlistLogging();
+ void reportWatchlistIfNecessary();
}
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index 3d3e148..5eda81b 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -43,6 +43,8 @@
/**
* Creates {@link NetworkStats} instances by parsing various {@code /proc/}
* files as needed.
+ *
+ * @hide
*/
public class NetworkStatsFactory {
private static final String TAG = "NetworkStatsFactory";
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 49de135..7af1b46 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3657,7 +3657,7 @@
</activity-alias>
<activity-alias android:name="com.android.internal.app.ForwardIntentToManagedProfile"
android:targetActivity="com.android.internal.app.IntentForwarderActivity"
- android:icon="@drawable/ic_corp_icon"
+ android:icon="@drawable/ic_corp_badge"
android:exported="true"
android:label="@string/managed_profile_label">
</activity-alias>
@@ -3944,6 +3944,10 @@
<service android:name="com.android.server.timezone.TimeZoneUpdateIdler"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
+
+ <service android:name="com.android.server.net.watchlist.ReportWatchlistJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
</application>
</manifest>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1c2e5a4..7cc43a7 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -876,7 +876,7 @@
<bool name="config_nightDisplayAvailable">@bool/config_setColorTransformAccelerated</bool>
<!-- Default mode to control how Night display is automatically activated.
- One of the following values (see NightDisplayController.java):
+ One of the following values (see ColorDisplayController.java):
0 - AUTO_MODE_DISABLED
1 - AUTO_MODE_CUSTOM
2 - AUTO_MODE_TWILIGHT
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 78a8e2a..9e0722b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4744,4 +4744,7 @@
A toast message shown when an app shortcut that wasn't restored due to an unknown issue is clicked,
-->
<string name="shortcut_restore_unknown_issue">Couldn\u2019t restore shortcut</string>
+
+ <!--Battery saver warning. STOPSHIP: Remove it eventually. -->
+ <string name="battery_saver_warning" translatable="false">Battery saver activated.\n\nSee go/extreme-battery-saver.\n\nThis contains aggressive experimental changes for P and may affect background app behavior.\n</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fd0012d..d630839 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1324,6 +1324,7 @@
<java-symbol type="drawable" name="cling_button" />
<java-symbol type="drawable" name="cling_arrow_up" />
<java-symbol type="drawable" name="cling_bg" />
+ <java-symbol type="drawable" name="ic_corp_badge" />
<java-symbol type="drawable" name="ic_corp_badge_color" />
<java-symbol type="drawable" name="ic_corp_badge_case" />
<java-symbol type="drawable" name="ic_corp_icon" />
@@ -3144,6 +3145,7 @@
<java-symbol type="string" name="shortcut_restore_not_supported" />
<java-symbol type="string" name="shortcut_restore_signature_mismatch" />
<java-symbol type="string" name="shortcut_restore_unknown_issue" />
+ <java-symbol type="string" name="battery_saver_warning" />
<!-- From media projection -->
<java-symbol type="string" name="config_mediaProjectionPermissionDialogComponent" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 1002939..d36ed63 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -26,7 +26,6 @@
import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isStatic;
-import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -264,6 +263,7 @@
Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE,
Settings.Global.NETWORK_AVOID_BAD_WIFI,
Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE,
+ Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME,
Settings.Global.NETWORK_PREFERENCE,
Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE,
Settings.Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS,
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f7e0b46..161a0c2 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -258,6 +258,11 @@
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
+ <privapp-permissions package="com.android.settings.intelligence">
+ <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.sharedstoragebackup">
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
</privapp-permissions>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index d90386f..21861692 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -94,7 +94,7 @@
public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
final int iconSize = UserIconDrawable.getSizeForList(context);
if (user.isManagedProfile()) {
- Drawable drawable = context.getDrawable(com.android.internal.R.drawable.ic_corp_icon);
+ Drawable drawable = context.getDrawable(com.android.internal.R.drawable.ic_corp_badge);
drawable.setBounds(0, 0, iconSize, iconSize);
return drawable;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
index 750601d..b27d823 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
@@ -57,7 +57,7 @@
if (userInfo.isManagedProfile()) {
mName = context.getString(R.string.managed_user_title);
icon = context.getDrawable(
- com.android.internal.R.drawable.ic_corp_icon);
+ com.android.internal.R.drawable.ic_corp_badge);
} else {
mName = userInfo.name;
final int userId = userInfo.id;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
similarity index 82%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl
rename to packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 8cf3be8..173a90a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.shared.recents.model;
+package com.android.systemui.shared.recents;
import android.view.MotionEvent;
+import com.android.systemui.shared.recents.ISystemUiProxy;
oneway interface IOverviewProxy {
+ void onBind(in ISystemUiProxy sysUiProxy);
void onMotionEvent(in MotionEvent event);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
new file mode 100644
index 0000000..d82b010
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 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.systemui.shared.recents;
+
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+
+/**
+ * Temporary callbacks into SystemUI.
+ */
+interface ISystemUiProxy {
+
+ /**
+ * Proxies SurfaceControl.screenshot().
+ */
+ Bitmap screenshot(in Rect sourceCrop, int width, int height,
+ int minLayer, int maxLayer, boolean useIdentityTransform,
+ int rotation);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 62a9319..e7e70af 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -26,7 +26,7 @@
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.Preconditions;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -205,8 +205,8 @@
mProviders.put(BatteryController.class, () ->
new BatteryControllerImpl(mContext));
- mProviders.put(NightDisplayController.class, () ->
- new NightDisplayController(mContext));
+ mProviders.put(ColorDisplayController.class, () ->
+ new ColorDisplayController(mContext));
mProviders.put(ManagedProfileController.class, () ->
new ManagedProfileControllerImpl(mContext));
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 528e242..9d960a1 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -20,12 +20,18 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
-import com.android.systemui.shared.recents.model.IOverviewProxy;
+import android.view.SurfaceControl;
+
+import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -46,6 +52,19 @@
private IOverviewProxy mOverviewProxy;
private int mConnectionBackoffAttempts;
+ private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
+ public Bitmap screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
+ boolean useIdentityTransform, int rotation) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ return SurfaceControl.screenshot(sourceCrop, width, height, minLayer, maxLayer,
+ useIdentityTransform, rotation);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+
private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -58,6 +77,11 @@
} catch (RemoteException e) {
Log.e(TAG, "Lost connection to launcher service", e);
}
+ try {
+ mOverviewProxy.onBind(mSysUiProxy);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to call onBind()", e);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 4c20361..763ffc6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -22,8 +22,7 @@
import android.service.quicksettings.Tile;
import android.widget.Switch;
-import com.android.internal.app.NightDisplayController;
-import com.android.internal.logging.MetricsLogger;
+import com.android.internal.app.ColorDisplayController;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.qs.QSHost;
@@ -31,19 +30,19 @@
import com.android.systemui.qs.tileimpl.QSTileImpl;
public class NightDisplayTile extends QSTileImpl<BooleanState>
- implements NightDisplayController.Callback {
+ implements ColorDisplayController.Callback {
- private NightDisplayController mController;
+ private ColorDisplayController mController;
private boolean mIsListening;
public NightDisplayTile(QSHost host) {
super(host);
- mController = new NightDisplayController(mContext, ActivityManager.getCurrentUser());
+ mController = new ColorDisplayController(mContext, ActivityManager.getCurrentUser());
}
@Override
public boolean isAvailable() {
- return NightDisplayController.isAvailable(mContext);
+ return ColorDisplayController.isAvailable(mContext);
}
@Override
@@ -65,7 +64,7 @@
}
// Make a new controller for the new user.
- mController = new NightDisplayController(mContext, newUserId);
+ mController = new ColorDisplayController(mContext, newUserId);
if (mIsListening) {
mController.setListener(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 1bd90fa..149ec0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -20,7 +20,7 @@
import android.provider.Settings.Secure;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
import com.android.systemui.Dependency;
import com.android.systemui.Prefs;
import com.android.systemui.Prefs.Key;
@@ -78,8 +78,8 @@
}
if (!mAutoTracker.isAdded(NIGHT)
- && NightDisplayController.isAvailable(mContext)) {
- Dependency.get(NightDisplayController.class).setListener(mNightDisplayCallback);
+ && ColorDisplayController.isAvailable(mContext)) {
+ Dependency.get(ColorDisplayController.class).setListener(mColorDisplayCallback);
}
}
@@ -89,7 +89,7 @@
Dependency.get(HotspotController.class).removeCallback(mHotspotCallback);
Dependency.get(DataSaverController.class).removeCallback(mDataSaverListener);
Dependency.get(ManagedProfileController.class).removeCallback(mProfileCallback);
- Dependency.get(NightDisplayController.class).setListener(null);
+ Dependency.get(ColorDisplayController.class).setListener(null);
}
private final ManagedProfileController.Callback mProfileCallback =
@@ -139,8 +139,8 @@
};
@VisibleForTesting
- final NightDisplayController.Callback mNightDisplayCallback =
- new NightDisplayController.Callback() {
+ final ColorDisplayController.Callback mColorDisplayCallback =
+ new ColorDisplayController.Callback() {
@Override
public void onActivated(boolean activated) {
if (activated) {
@@ -150,8 +150,8 @@
@Override
public void onAutoModeChanged(int autoMode) {
- if (autoMode == NightDisplayController.AUTO_MODE_CUSTOM
- || autoMode == NightDisplayController.AUTO_MODE_TWILIGHT) {
+ if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM
+ || autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
addNightTile();
}
}
@@ -160,7 +160,7 @@
if (mAutoTracker.isAdded(NIGHT)) return;
mHost.addTile(NIGHT);
mAutoTracker.setTileAdded(NIGHT);
- mHandler.post(() -> Dependency.get(NightDisplayController.class)
+ mHandler.post(() -> Dependency.get(ColorDisplayController.class)
.setListener(null));
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 3ba9ac6..717699b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -35,7 +35,7 @@
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
-import com.android.systemui.shared.recents.model.IOverviewProxy;
+import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.tuner.TunerService;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 3b401a5..a95e3db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -24,7 +24,7 @@
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
import com.android.systemui.Dependency;
import com.android.systemui.Prefs;
import com.android.systemui.Prefs.Key;
@@ -61,49 +61,49 @@
@Test
public void nightTileAdded_whenActivated() {
- if (!NightDisplayController.isAvailable(mContext)) {
+ if (!ColorDisplayController.isAvailable(mContext)) {
return;
}
- mAutoTileManager.mNightDisplayCallback.onActivated(true);
+ mAutoTileManager.mColorDisplayCallback.onActivated(true);
verify(mQsTileHost).addTile("night");
}
@Test
public void nightTileNotAdded_whenDeactivated() {
- if (!NightDisplayController.isAvailable(mContext)) {
+ if (!ColorDisplayController.isAvailable(mContext)) {
return;
}
- mAutoTileManager.mNightDisplayCallback.onActivated(false);
+ mAutoTileManager.mColorDisplayCallback.onActivated(false);
verify(mQsTileHost, never()).addTile("night");
}
@Test
public void nightTileAdded_whenNightModeTwilight() {
- if (!NightDisplayController.isAvailable(mContext)) {
+ if (!ColorDisplayController.isAvailable(mContext)) {
return;
}
- mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
- NightDisplayController.AUTO_MODE_TWILIGHT);
+ mAutoTileManager.mColorDisplayCallback.onAutoModeChanged(
+ ColorDisplayController.AUTO_MODE_TWILIGHT);
verify(mQsTileHost).addTile("night");
}
@Test
public void nightTileAdded_whenNightModeCustom() {
- if (!NightDisplayController.isAvailable(mContext)) {
+ if (!ColorDisplayController.isAvailable(mContext)) {
return;
}
- mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
- NightDisplayController.AUTO_MODE_CUSTOM);
+ mAutoTileManager.mColorDisplayCallback.onAutoModeChanged(
+ ColorDisplayController.AUTO_MODE_CUSTOM);
verify(mQsTileHost).addTile("night");
}
@Test
public void nightTileNotAdded_whenNightModeDisabled() {
- if (!NightDisplayController.isAvailable(mContext)) {
+ if (!ColorDisplayController.isAvailable(mContext)) {
return;
}
- mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
- NightDisplayController.AUTO_MODE_DISABLED);
+ mAutoTileManager.mColorDisplayCallback.onAutoModeChanged(
+ ColorDisplayController.AUTO_MODE_DISABLED);
verify(mQsTileHost, never()).addTile("night");
}
}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index d3dcf5a..8e93d19 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4680,6 +4680,16 @@
// OS: P
DIALOG_FIRMWARE_VERSION = 1247;
+ // OPEN: Settings > Network & internet > Menu > Private DNS
+ // CATEGORY: SETTINGS
+ // OS: P
+ DIALOG_PRIVATE_DNS = 1248;
+
+ // ACTION: A private dns mode been selected by user
+ // CATEGORY: SETTINGS
+ // OS: P
+ ACTION_PRIVATE_DNS_MODE = 1249;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/services/art-profile b/services/art-profile
index ac5ecaf..301918f 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -2374,6 +2374,9 @@
HPLcom/android/server/display/AutomaticBrightnessController;->updateAmbientLux()V
HPLcom/android/server/display/AutomaticBrightnessController;->updateAmbientLux(J)V
HPLcom/android/server/display/AutomaticBrightnessController;->weightIntegral(J)F
+HPLcom/android/server/display/ColorDisplayService$3;->onAnimationUpdate(Landroid/animation/ValueAnimator;)V
+HPLcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;->evaluate(FLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+HPLcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;->evaluate(F[F[F)[F
HPLcom/android/server/display/DisplayBlanker;->requestDisplayState(II)V
HPLcom/android/server/display/DisplayManagerService$BinderService;->getDisplayIds()[I
HPLcom/android/server/display/DisplayManagerService$CallbackRecord;->binderDied()V
@@ -2430,9 +2433,6 @@
HPLcom/android/server/display/LogicalDisplay;->getRequestedModeIdLocked()I
HPLcom/android/server/display/LogicalDisplay;->hasContentLocked()Z
HPLcom/android/server/display/LogicalDisplay;->setDisplayInfoOverrideFromWindowManagerLocked(Landroid/view/DisplayInfo;)Z
-HPLcom/android/server/display/NightDisplayService$3;->onAnimationUpdate(Landroid/animation/ValueAnimator;)V
-HPLcom/android/server/display/NightDisplayService$ColorMatrixEvaluator;->evaluate(FLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLcom/android/server/display/NightDisplayService$ColorMatrixEvaluator;->evaluate(F[F[F)[F
HPLcom/android/server/display/RampAnimator$1;->run()V
HPLcom/android/server/display/RampAnimator$Listener;->onAnimationEnd()V
HPLcom/android/server/display/RampAnimator;->-get0(Lcom/android/server/display/RampAnimator;)F
@@ -10133,6 +10133,25 @@
PLcom/android/server/display/AutomaticBrightnessController;->setLightSensorEnabled(Z)Z
PLcom/android/server/display/AutomaticBrightnessController;->setScreenAutoBrightnessAdjustment(F)Z
PLcom/android/server/display/AutomaticBrightnessController;->updateAutoBrightness(Z)V
+PLcom/android/server/display/ColorDisplayService$1;-><init>(Lcom/android/server/display/ColorDisplayService;)V
+PLcom/android/server/display/ColorDisplayService$3;-><init>(Lcom/android/server/display/ColorDisplayService;Lcom/android/server/display/DisplayTransformManager;)V
+PLcom/android/server/display/ColorDisplayService$4;-><init>(Lcom/android/server/display/ColorDisplayService;Lcom/android/server/display/DisplayTransformManager;[F)V
+PLcom/android/server/display/ColorDisplayService$4;->onAnimationCancel(Landroid/animation/Animator;)V
+PLcom/android/server/display/ColorDisplayService$4;->onAnimationEnd(Landroid/animation/Animator;)V
+PLcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;-><init>()V
+PLcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;-><init>(Lcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;)V
+PLcom/android/server/display/ColorDisplayService;->-set0(Lcom/android/server/display/ColorDisplayService;Landroid/animation/ValueAnimator;)Landroid/animation/ValueAnimator;
+PLcom/android/server/display/ColorDisplayService;-><init>(Landroid/content/Context;)V
+PLcom/android/server/display/ColorDisplayService;->applyTint(Z)V
+PLcom/android/server/display/ColorDisplayService;->isUserSetupCompleted(Landroid/content/ContentResolver;I)Z
+PLcom/android/server/display/ColorDisplayService;->onActivated(Z)V
+PLcom/android/server/display/ColorDisplayService;->onAutoModeChanged(I)V
+PLcom/android/server/display/ColorDisplayService;->onBootPhase(I)V
+PLcom/android/server/display/ColorDisplayService;->onStart()V
+PLcom/android/server/display/ColorDisplayService;->onStartUser(I)V
+PLcom/android/server/display/ColorDisplayService;->onUserChanged(I)V
+PLcom/android/server/display/ColorDisplayService;->setMatrix(I[F)V
+PLcom/android/server/display/ColorDisplayService;->setUp()V
PLcom/android/server/display/ColorFade;-><init>(I)V
PLcom/android/server/display/ColorFade;->createNativeFloatBuffer(I)Ljava/nio/FloatBuffer;
PLcom/android/server/display/ColorFade;->dismiss()V
@@ -10230,25 +10249,6 @@
PLcom/android/server/display/LogicalDisplay;->getDisplayIdLocked()I
PLcom/android/server/display/LogicalDisplay;->getNonOverrideDisplayInfoLocked(Landroid/view/DisplayInfo;)V
PLcom/android/server/display/LogicalDisplay;->setHasContentLocked(Z)V
-PLcom/android/server/display/NightDisplayService$1;-><init>(Lcom/android/server/display/NightDisplayService;)V
-PLcom/android/server/display/NightDisplayService$3;-><init>(Lcom/android/server/display/NightDisplayService;Lcom/android/server/display/DisplayTransformManager;)V
-PLcom/android/server/display/NightDisplayService$4;-><init>(Lcom/android/server/display/NightDisplayService;Lcom/android/server/display/DisplayTransformManager;[F)V
-PLcom/android/server/display/NightDisplayService$4;->onAnimationCancel(Landroid/animation/Animator;)V
-PLcom/android/server/display/NightDisplayService$4;->onAnimationEnd(Landroid/animation/Animator;)V
-PLcom/android/server/display/NightDisplayService$ColorMatrixEvaluator;-><init>()V
-PLcom/android/server/display/NightDisplayService$ColorMatrixEvaluator;-><init>(Lcom/android/server/display/NightDisplayService$ColorMatrixEvaluator;)V
-PLcom/android/server/display/NightDisplayService;->-set0(Lcom/android/server/display/NightDisplayService;Landroid/animation/ValueAnimator;)Landroid/animation/ValueAnimator;
-PLcom/android/server/display/NightDisplayService;-><init>(Landroid/content/Context;)V
-PLcom/android/server/display/NightDisplayService;->applyTint(Z)V
-PLcom/android/server/display/NightDisplayService;->isUserSetupCompleted(Landroid/content/ContentResolver;I)Z
-PLcom/android/server/display/NightDisplayService;->onActivated(Z)V
-PLcom/android/server/display/NightDisplayService;->onAutoModeChanged(I)V
-PLcom/android/server/display/NightDisplayService;->onBootPhase(I)V
-PLcom/android/server/display/NightDisplayService;->onStart()V
-PLcom/android/server/display/NightDisplayService;->onStartUser(I)V
-PLcom/android/server/display/NightDisplayService;->onUserChanged(I)V
-PLcom/android/server/display/NightDisplayService;->setMatrix(I[F)V
-PLcom/android/server/display/NightDisplayService;->setUp()V
PLcom/android/server/display/OverlayDisplayAdapter$1$1;-><init>(Lcom/android/server/display/OverlayDisplayAdapter$1;Landroid/os/Handler;)V
PLcom/android/server/display/OverlayDisplayAdapter$1;-><init>(Lcom/android/server/display/OverlayDisplayAdapter;)V
PLcom/android/server/display/OverlayDisplayAdapter$1;->run()V
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index e609bd7..831c9cb 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1356,31 +1356,6 @@
}
}
- public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData) {
- if (!checkNotifyPermission("notifyOemHookRawEventForSubscriber")) {
- return;
- }
-
- synchronized (mRecords) {
- for (Record r : mRecords) {
- if (VDBG) {
- log("notifyOemHookRawEventForSubscriber: r=" + r + " subId=" + subId);
- }
- if ((r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT)) &&
- ((r.subId == subId) ||
- (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID))) {
- try {
- r.callback.onOemHookRawEvent(rawData);
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
- }
- }
- }
- handleRemoveListLocked();
- }
- }
-
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -1673,11 +1648,6 @@
android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
}
-
- if ((events & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
- }
}
private void handleRemoveListLocked() {
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index c04ddf8..ea7b443 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -404,8 +404,8 @@
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
|| windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- return supportsSplitScreen && WindowConfiguration.supportSplitScreenWindowingMode(
- windowingMode, activityType);
+ return supportsSplitScreen
+ && WindowConfiguration.supportSplitScreenWindowingMode(activityType);
}
if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 88403fc..8cc584e 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -870,6 +871,29 @@
}
}
+ /**
+ * @param reason The reason for moving the stack to the back.
+ * @param task If non-null, the task will be moved to the bottom of the stack.
+ **/
+ void moveToBack(String reason, TaskRecord task) {
+ if (!isAttached()) {
+ return;
+ }
+
+ getDisplay().positionChildAtBottom(this);
+ mStackSupervisor.setFocusStackUnchecked(reason, getDisplay().getTopStack());
+ if (task != null) {
+ insertTaskAtBottom(task);
+ return;
+ } else {
+ task = bottomTask();
+ if (task != null) {
+ mWindowContainerController.positionChildAtBottom(
+ task.getWindowContainerController(), true /* includingParents */);
+ }
+ }
+ }
+
boolean isFocusable() {
if (getWindowConfiguration().canReceiveKeys()) {
return true;
@@ -1778,6 +1802,15 @@
return inPinnedWindowingMode();
}
+ /** @return True if the resizing of the primary-split-screen stack affects this stack size. */
+ boolean affectedBySplitScreenResize() {
+ if (!supportsSplitScreenWindowingMode()) {
+ return false;
+ }
+ final int windowingMode = getWindowingMode();
+ return windowingMode != WINDOWING_MODE_FREEFORM && windowingMode != WINDOWING_MODE_PINNED;
+ }
+
/**
* @return the top most visible activity that wants to dismiss Keyguard
*/
@@ -2581,6 +2614,9 @@
if (position >= mTaskHistory.size()) {
insertTaskAtTop(task, null);
return;
+ } else if (position <= 0) {
+ insertTaskAtBottom(task);
+ return;
}
position = getAdjustedPositionForTask(task, position, null /* starting */);
mTaskHistory.remove(task);
@@ -2601,6 +2637,16 @@
true /* includingParents */);
}
+ private void insertTaskAtBottom(TaskRecord task) {
+ // Unlike insertTaskAtPosition, this will also position parents of the windowcontroller.
+ mTaskHistory.remove(task);
+ final int position = getAdjustedPositionForTask(task, 0, null);
+ mTaskHistory.add(position, task);
+ updateTaskMovement(task, true);
+ mWindowContainerController.positionChildAtBottom(task.getWindowContainerController(),
+ true /* includingParents */);
+ }
+
final void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
boolean newTask, boolean keepCurTransition, ActivityOptions options) {
TaskRecord rTask = r.getTask();
@@ -4370,8 +4416,7 @@
updateTaskMovement(tr, false);
mWindowManager.prepareAppTransition(TRANSIT_TASK_TO_BACK, false);
- mWindowContainerController.positionChildAtBottom(tr.getWindowContainerController(),
- true /* includingParents */);
+ moveToBack("moveTaskToBackLocked", tr);
if (inPinnedWindowingMode()) {
mStackSupervisor.removeStack(this);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index a0aeb63..062083c 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2441,7 +2441,7 @@
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId);
mWindowManager.deferSurfaceLayout();
try {
- if (stack.supportsSplitScreenWindowingMode()) {
+ if (stack.affectedBySplitScreenResize()) {
if (bounds == null && stack.inSplitScreenWindowingMode()) {
// null bounds = fullscreen windowing mode...at least for now.
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -2541,8 +2541,6 @@
null, mTmpOptions, task, task.getActivityType(), onTop);
if (onTop) {
- final int returnToType =
- toDisplay.getTopVisibleStackActivityType(WINDOWING_MODE_PINNED);
final boolean isTopTask = i == (size - 1);
// Defer resume until all the tasks have been moved to the fullscreen stack
task.reparent(toStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
@@ -2631,7 +2629,7 @@
if (current.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
continue;
}
- if (!current.supportsSplitScreenWindowingMode()) {
+ if (!current.affectedBySplitScreenResize()) {
continue;
}
// Need to set windowing mode here before we try to get the dock bounds.
@@ -4177,8 +4175,8 @@
}
// Handle incorrect launch/move to secondary display if needed.
- final boolean launchOnSecondaryDisplayFailed;
if (isSecondaryDisplayPreferred) {
+ final boolean launchOnSecondaryDisplayFailed;
final int actualDisplayId = task.getStack().mDisplayId;
if (!task.canBeLaunchedOnDisplay(actualDisplayId)) {
// The task landed on an inappropriate display somehow, move it to the default
@@ -4193,34 +4191,34 @@
|| (preferredDisplayId != INVALID_DISPLAY
&& preferredDisplayId != actualDisplayId);
}
- } else {
- // The task wasn't requested to be on a secondary display.
- launchOnSecondaryDisplayFailed = false;
- }
-
- final ActivityRecord topActivity = task.getTopActivity();
- if (launchOnSecondaryDisplayFailed
- || !task.supportsSplitScreenWindowingMode() || forceNonResizable) {
if (launchOnSecondaryDisplayFailed) {
// Display a warning toast that we tried to put a non-resizeable task on a secondary
// display with config different from global config.
mService.mTaskChangeNotificationController
.notifyActivityLaunchOnSecondaryDisplayFailed();
- } else {
- // Display a warning toast that we tried to put a non-dockable task in the docked
- // stack.
- mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
+ return;
}
+ }
+
+ final ActivityRecord topActivity = task.getTopActivity();
+ if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) {
+ // Display a warning toast that we tried to put a non-dockable task in the docked
+ // stack.
+ mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
// Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
// we need to move it to top of fullscreen stack, otherwise it will be covered.
- final ActivityStack dockedStack = task.getStack().getDisplay().getSplitScreenPrimaryStack();
+ final ActivityStack dockedStack =
+ task.getStack().getDisplay().getSplitScreenPrimaryStack();
if (dockedStack != null) {
moveTasksToFullscreenStackLocked(dockedStack, actualStack == dockedStack);
}
- } else if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
- && !topActivity.noDisplay) {
+ return;
+ }
+
+ if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
+ && !topActivity.noDisplay) {
final String packageName = topActivity.appInfo.packageName;
final int reason = isSecondaryDisplayPreferred
? FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY
@@ -4489,9 +4487,12 @@
"startActivityFromRecents: Task " + taskId + " not found.");
}
- // We always want to return to the home activity instead of the recents activity from
- // whatever is started from the recents activity, so move the home stack forward.
- moveHomeStackToFront("startActivityFromRecents");
+ if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ // We always want to return to the home activity instead of the recents activity
+ // from whatever is started from the recents activity, so move the home stack
+ // forward.
+ moveHomeStackToFront("startActivityFromRecents");
+ }
// If the user must confirm credentials (e.g. when first launching a work app and the
// Work Challenge is present) let startActivityInPackage handle the intercepting.
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index 5cc390a..f427819 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -23,8 +23,6 @@
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.IpConnectivityLog;
import android.os.Binder;
-import android.os.IBinder;
-import android.os.Parcelable;
import android.os.Process;
import android.provider.Settings;
import android.text.TextUtils;
@@ -322,22 +320,22 @@
}
@Override
- public boolean registerNetdEventCallback(INetdEventCallback callback) {
+ public boolean addNetdEventCallback(int callerType, INetdEventCallback callback) {
enforceNetdEventListeningPermission();
if (mNetdListener == null) {
return false;
}
- return mNetdListener.registerNetdEventCallback(callback);
+ return mNetdListener.addNetdEventCallback(callerType, callback);
}
@Override
- public boolean unregisterNetdEventCallback() {
+ public boolean removeNetdEventCallback(int callerType) {
enforceNetdEventListeningPermission();
if (mNetdListener == null) {
// if the service is null, we aren't registered anyway
return true;
}
- return mNetdListener.unregisterNetdEventCallback();
+ return mNetdListener.removeNetdEventCallback(callerType);
}
};
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 61b11e1..af138b93 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -98,21 +98,55 @@
@GuardedBy("this")
private final TokenBucket mConnectTb =
new TokenBucket(CONNECT_LATENCY_FILL_RATE, CONNECT_LATENCY_BURST_LIMIT);
- // Callback should only be registered/unregistered when logging is being enabled/disabled in DPM
- // by the device owner. It's DevicePolicyManager's responsibility to ensure that.
- @GuardedBy("this")
- private INetdEventCallback mNetdEventCallback;
- public synchronized boolean registerNetdEventCallback(INetdEventCallback callback) {
- mNetdEventCallback = callback;
+
+ /**
+ * There are only 2 possible callbacks.
+ *
+ * mNetdEventCallbackList[CALLBACK_CALLER_DEVICE_POLICY].
+ * Callback registered/unregistered when logging is being enabled/disabled in DPM
+ * by the device owner. It's DevicePolicyManager's responsibility to ensure that.
+ *
+ * mNetdEventCallbackList[CALLBACK_CALLER_NETWORK_WATCHLIST]
+ * Callback registered/unregistered by NetworkWatchlistService.
+ */
+ @GuardedBy("this")
+ private static final int[] ALLOWED_CALLBACK_TYPES = {
+ INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY,
+ INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST
+ };
+
+ @GuardedBy("this")
+ private INetdEventCallback[] mNetdEventCallbackList =
+ new INetdEventCallback[ALLOWED_CALLBACK_TYPES.length];
+
+ public synchronized boolean addNetdEventCallback(int callerType, INetdEventCallback callback) {
+ if (!isValidCallerType(callerType)) {
+ Log.e(TAG, "Invalid caller type: " + callerType);
+ return false;
+ }
+ mNetdEventCallbackList[callerType] = callback;
return true;
}
- public synchronized boolean unregisterNetdEventCallback() {
- mNetdEventCallback = null;
+ public synchronized boolean removeNetdEventCallback(int callerType) {
+ if (!isValidCallerType(callerType)) {
+ Log.e(TAG, "Invalid caller type: " + callerType);
+ return false;
+ }
+ mNetdEventCallbackList[callerType] = null;
return true;
}
+ private static boolean isValidCallerType(int callerType) {
+ for (int i = 0; i < ALLOWED_CALLBACK_TYPES.length; i++) {
+ if (callerType == ALLOWED_CALLBACK_TYPES[i]) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public NetdEventListenerService(Context context) {
this(context.getSystemService(ConnectivityManager.class));
}
@@ -169,8 +203,10 @@
long timestamp = System.currentTimeMillis();
getMetricsForNetwork(timestamp, netId).addDnsResult(eventType, returnCode, latencyMs);
- if (mNetdEventCallback != null) {
- mNetdEventCallback.onDnsEvent(hostname, ipAddresses, ipAddressesCount, timestamp, uid);
+ for (INetdEventCallback callback : mNetdEventCallbackList) {
+ if (callback != null) {
+ callback.onDnsEvent(hostname, ipAddresses, ipAddressesCount, timestamp, uid);
+ }
}
}
@@ -184,8 +220,14 @@
long timestamp = System.currentTimeMillis();
getMetricsForNetwork(timestamp, netId).addConnectResult(error, latencyMs, ipAddr);
- if (mNetdEventCallback != null) {
- mNetdEventCallback.onConnectEvent(ipAddr, port, timestamp, uid);
+ for (INetdEventCallback callback : mNetdEventCallbackList) {
+ if (callback != null) {
+ // TODO(rickywai): Remove this checking to collect ip in watchlist.
+ if (callback ==
+ mNetdEventCallbackList[INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY]) {
+ callback.onConnectEvent(ipAddr, port, timestamp, uid);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/display/NightDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
similarity index 97%
rename from services/core/java/com/android/server/display/NightDisplayService.java
rename to services/core/java/com/android/server/display/ColorDisplayService.java
index a7c3ff9..af8ecad 100644
--- a/services/core/java/com/android/server/display/NightDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -42,7 +42,7 @@
import android.util.Slog;
import android.view.animation.AnimationUtils;
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
import com.android.server.SystemService;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
@@ -60,10 +60,10 @@
/**
* Tints the display at night.
*/
-public final class NightDisplayService extends SystemService
- implements NightDisplayController.Callback {
+public final class ColorDisplayService extends SystemService
+ implements ColorDisplayController.Callback {
- private static final String TAG = "NightDisplayService";
+ private static final String TAG = "ColorDisplayService";
/**
* The transition time, in milliseconds, for Night Display to turn on/off.
@@ -119,12 +119,12 @@
private ContentObserver mUserSetupObserver;
private boolean mBootCompleted;
- private NightDisplayController mController;
+ private ColorDisplayController mController;
private ValueAnimator mColorMatrixAnimator;
private Boolean mIsActivated;
private AutoMode mAutoMode;
- public NightDisplayService(Context context) {
+ public ColorDisplayService(Context context) {
super(context);
mHandler = new Handler(Looper.getMainLooper());
}
@@ -228,7 +228,7 @@
Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
// Create a new controller for the current user and start listening for changes.
- mController = new NightDisplayController(getContext(), mCurrentUser);
+ mController = new ColorDisplayController(getContext(), mCurrentUser);
mController.setListener(this);
setCoefficientMatrix(getContext());
@@ -293,9 +293,9 @@
mAutoMode = null;
}
- if (autoMode == NightDisplayController.AUTO_MODE_CUSTOM) {
+ if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
mAutoMode = new CustomAutoMode();
- } else if (autoMode == NightDisplayController.AUTO_MODE_TWILIGHT) {
+ } else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
mAutoMode = new TwilightAutoMode();
}
@@ -463,7 +463,7 @@
return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
}
- private abstract class AutoMode implements NightDisplayController.Callback {
+ private abstract class AutoMode implements ColorDisplayController.Callback {
public abstract void onStart();
public abstract void onStop();
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index bef6898..338e331 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -29,7 +29,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
import java.util.Arrays;
/**
@@ -223,13 +223,13 @@
}
public boolean setColorMode(int colorMode) {
- if (colorMode == NightDisplayController.COLOR_MODE_NATURAL) {
+ if (colorMode == ColorDisplayController.COLOR_MODE_NATURAL) {
applySaturation(COLOR_SATURATION_NATURAL);
setNativeMode(false);
- } else if (colorMode == NightDisplayController.COLOR_MODE_BOOSTED) {
+ } else if (colorMode == ColorDisplayController.COLOR_MODE_BOOSTED) {
applySaturation(COLOR_SATURATION_BOOSTED);
setNativeMode(false);
- } else if (colorMode == NightDisplayController.COLOR_MODE_SATURATED) {
+ } else if (colorMode == ColorDisplayController.COLOR_MODE_SATURATED) {
applySaturation(COLOR_SATURATION_NATURAL);
setNativeMode(true);
}
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index 39f2a96..caa8522 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -174,7 +174,7 @@
private final class AppIdleStateChangeListener
extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
- public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
+ public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) {
boolean changed = false;
synchronized (mLock) {
if (mAppIdleParoleOn) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 551cb10..3fa3cd4 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3746,7 +3746,7 @@
extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
- public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
+ public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) {
try {
final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
diff --git a/services/core/java/com/android/server/net/watchlist/DigestUtils.java b/services/core/java/com/android/server/net/watchlist/DigestUtils.java
new file mode 100644
index 0000000..57becb0
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/DigestUtils.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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.net.watchlist;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Utils for calculating digests.
+ */
+public class DigestUtils {
+
+ private static final int FILE_READ_BUFFER_SIZE = 16 * 1024;
+
+ private DigestUtils() {}
+
+ /** @return SHA256 hash of the provided file */
+ public static byte[] getSha256Hash(File apkFile) throws IOException, NoSuchAlgorithmException {
+ try (InputStream stream = new FileInputStream(apkFile)) {
+ return getSha256Hash(stream);
+ }
+ }
+
+ /** @return SHA256 hash of data read from the provided input stream */
+ public static byte[] getSha256Hash(InputStream stream)
+ throws IOException, NoSuchAlgorithmException {
+ MessageDigest digester = MessageDigest.getInstance("SHA256");
+
+ int bytesRead;
+ byte[] buf = new byte[FILE_READ_BUFFER_SIZE];
+ while ((bytesRead = stream.read(buf)) >= 0) {
+ digester.update(buf, 0, bytesRead);
+ }
+ return digester.digest();
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/net/watchlist/HarmfulDigests.java b/services/core/java/com/android/server/net/watchlist/HarmfulDigests.java
new file mode 100644
index 0000000..27c22ce
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/HarmfulDigests.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 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.net.watchlist;
+
+import com.android.internal.util.HexDump;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Helper class to store all harmful digests in memory.
+ * TODO: Optimize memory usage using byte array with binary search.
+ */
+class HarmfulDigests {
+
+ private final Set<String> mDigestSet;
+
+ HarmfulDigests(List<byte[]> digests) {
+ final HashSet<String> tmpDigestSet = new HashSet<>();
+ final int size = digests.size();
+ for (int i = 0; i < size; i++) {
+ tmpDigestSet.add(HexDump.toHexString(digests.get(i)));
+ }
+ mDigestSet = Collections.unmodifiableSet(tmpDigestSet);
+ }
+
+ public boolean contains(byte[] digest) {
+ return mDigestSet.contains(HexDump.toHexString(digest));
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ for (String digest : mDigestSet) {
+ pw.println(digest);
+ }
+ pw.println("");
+ }
+}
diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
new file mode 100644
index 0000000..171703a
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2017 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.net.watchlist;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.IIpConnectivityMetrics;
+import android.net.INetdEventCallback;
+import android.net.NetworkWatchlistManager;
+import android.net.metrics.IpConnectivityLog;
+import android.os.Binder;
+import android.os.Process;
+import android.os.SharedMemory;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.net.INetworkWatchlistManager;
+import com.android.server.ServiceThread;
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Implementation of network watchlist service.
+ */
+public class NetworkWatchlistService extends INetworkWatchlistManager.Stub {
+
+ private static final String TAG = NetworkWatchlistService.class.getSimpleName();
+ static final boolean DEBUG = false;
+
+ private static final String PROPERTY_NETWORK_WATCHLIST_ENABLED =
+ "ro.network_watchlist_enabled";
+
+ private static final int MAX_NUM_OF_WATCHLIST_DIGESTS = 10000;
+
+ public static class Lifecycle extends SystemService {
+ private NetworkWatchlistService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ if (!SystemProperties.getBoolean(PROPERTY_NETWORK_WATCHLIST_ENABLED, false)) {
+ // Watchlist service is disabled
+ return;
+ }
+ mService = new NetworkWatchlistService(getContext());
+ publishBinderService(Context.NETWORK_WATCHLIST_SERVICE, mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (!SystemProperties.getBoolean(PROPERTY_NETWORK_WATCHLIST_ENABLED, false)) {
+ // Watchlist service is disabled
+ return;
+ }
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ try {
+ mService.initIpConnectivityMetrics();
+ mService.startWatchlistLogging();
+ } catch (RemoteException e) {
+ // Should not happen
+ }
+ ReportWatchlistJobService.schedule(getContext());
+ }
+ }
+ }
+
+ private volatile boolean mIsLoggingEnabled = false;
+ private final Object mLoggingSwitchLock = new Object();
+
+ private final WatchlistSettings mSettings;
+ private final Context mContext;
+
+ // Separate thread to handle expensive watchlist logging work.
+ private final ServiceThread mHandlerThread;
+
+ @VisibleForTesting
+ IIpConnectivityMetrics mIpConnectivityMetrics;
+ @VisibleForTesting
+ WatchlistLoggingHandler mNetworkWatchlistHandler;
+
+ public NetworkWatchlistService(Context context) {
+ mContext = context;
+ mSettings = WatchlistSettings.getInstance();
+ mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
+ /* allowIo */ false);
+ mHandlerThread.start();
+ mNetworkWatchlistHandler = new WatchlistLoggingHandler(mContext,
+ mHandlerThread.getLooper());
+ mNetworkWatchlistHandler.reportWatchlistIfNecessary();
+ }
+
+ // For testing only
+ @VisibleForTesting
+ NetworkWatchlistService(Context context, ServiceThread handlerThread,
+ WatchlistLoggingHandler handler, IIpConnectivityMetrics ipConnectivityMetrics) {
+ mContext = context;
+ mSettings = WatchlistSettings.getInstance();
+ mHandlerThread = handlerThread;
+ mNetworkWatchlistHandler = handler;
+ mIpConnectivityMetrics = ipConnectivityMetrics;
+ }
+
+ private void initIpConnectivityMetrics() {
+ mIpConnectivityMetrics = (IIpConnectivityMetrics) IIpConnectivityMetrics.Stub.asInterface(
+ ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
+ }
+
+ private final INetdEventCallback mNetdEventCallback = new INetdEventCallback.Stub() {
+ @Override
+ public void onDnsEvent(String hostname, String[] ipAddresses, int ipAddressesCount,
+ long timestamp, int uid) {
+ if (!mIsLoggingEnabled) {
+ return;
+ }
+ mNetworkWatchlistHandler.asyncNetworkEvent(hostname, ipAddresses, uid);
+ }
+
+ @Override
+ public void onConnectEvent(String ipAddr, int port, long timestamp, int uid) {
+ if (!mIsLoggingEnabled) {
+ return;
+ }
+ mNetworkWatchlistHandler.asyncNetworkEvent(null, new String[]{ipAddr}, uid);
+ }
+ };
+
+ @VisibleForTesting
+ protected boolean startWatchlistLoggingImpl() throws RemoteException {
+ if (DEBUG) {
+ Slog.i(TAG, "Starting watchlist logging.");
+ }
+ synchronized (mLoggingSwitchLock) {
+ if (mIsLoggingEnabled) {
+ Slog.w(TAG, "Watchlist logging is already running");
+ return true;
+ }
+ try {
+ if (mIpConnectivityMetrics.addNetdEventCallback(
+ INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST, mNetdEventCallback)) {
+ mIsLoggingEnabled = true;
+ return true;
+ } else {
+ return false;
+ }
+ } catch (RemoteException re) {
+ // Should not happen
+ return false;
+ }
+ }
+ }
+
+ @Override
+ public boolean startWatchlistLogging() throws RemoteException {
+ enforceWatchlistLoggingPermission();
+ return startWatchlistLoggingImpl();
+ }
+
+ @VisibleForTesting
+ protected boolean stopWatchlistLoggingImpl() {
+ if (DEBUG) {
+ Slog.i(TAG, "Stopping watchlist logging");
+ }
+ synchronized (mLoggingSwitchLock) {
+ if (!mIsLoggingEnabled) {
+ Slog.w(TAG, "Watchlist logging is not running");
+ return true;
+ }
+ // stop the logging regardless of whether we fail to unregister listener
+ mIsLoggingEnabled = false;
+
+ try {
+ return mIpConnectivityMetrics.removeNetdEventCallback(
+ INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST);
+ } catch (RemoteException re) {
+ // Should not happen
+ return false;
+ }
+ }
+ }
+
+ @Override
+ public boolean stopWatchlistLogging() throws RemoteException {
+ enforceWatchlistLoggingPermission();
+ return stopWatchlistLoggingImpl();
+ }
+
+ private void enforceWatchlistLoggingPermission() {
+ final int uid = Binder.getCallingUid();
+ if (uid != Process.SYSTEM_UID) {
+ throw new SecurityException(String.format("Uid %d has no permission to change watchlist"
+ + " setting.", uid));
+ }
+ }
+
+ /**
+ * Set a new network watchlist.
+ * This method should be called by ConfigUpdater only.
+ *
+ * @return True if network watchlist is updated.
+ */
+ public boolean setNetworkSecurityWatchlist(List<byte[]> domainsCrc32Digests,
+ List<byte[]> domainsSha256Digests,
+ List<byte[]> ipAddressesCrc32Digests,
+ List<byte[]> ipAddressesSha256Digests) {
+ Slog.i(TAG, "Setting network watchlist");
+ if (domainsCrc32Digests == null || domainsSha256Digests == null
+ || ipAddressesCrc32Digests == null || ipAddressesSha256Digests == null) {
+ Slog.e(TAG, "Parameters cannot be null");
+ return false;
+ }
+ if (domainsCrc32Digests.size() != domainsSha256Digests.size()
+ || ipAddressesCrc32Digests.size() != ipAddressesSha256Digests.size()) {
+ Slog.e(TAG, "Must need to have the same number of CRC32 and SHA256 digests");
+ return false;
+ }
+ if (domainsSha256Digests.size() + ipAddressesSha256Digests.size()
+ > MAX_NUM_OF_WATCHLIST_DIGESTS) {
+ Slog.e(TAG, "Total watchlist size cannot exceed " + MAX_NUM_OF_WATCHLIST_DIGESTS);
+ return false;
+ }
+ mSettings.writeSettingsToDisk(domainsCrc32Digests, domainsSha256Digests,
+ ipAddressesCrc32Digests, ipAddressesSha256Digests);
+ Slog.i(TAG, "Set network watchlist: Success");
+ return true;
+ }
+
+ @Override
+ public void reportWatchlistIfNecessary() {
+ // Allow any apps to trigger report event, as we won't run it if it's too early.
+ mNetworkWatchlistHandler.reportWatchlistIfNecessary();
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ mSettings.dump(fd, pw, args);
+ }
+
+}
diff --git a/services/core/java/com/android/server/net/watchlist/ReportWatchlistJobService.java b/services/core/java/com/android/server/net/watchlist/ReportWatchlistJobService.java
new file mode 100644
index 0000000..dfeb1b2
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/ReportWatchlistJobService.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 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.net.watchlist;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.NetworkWatchlistManager;
+import android.util.Slog;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A job that periodically report watchlist records.
+ */
+public class ReportWatchlistJobService extends JobService {
+
+ private static final boolean DEBUG = NetworkWatchlistService.DEBUG;
+ private static final String TAG = "WatchlistJobService";
+
+ // Unique job id used in system service, other jobs should not use the same value.
+ public static final int REPORT_WATCHLIST_RECORDS_JOB_ID = 0xd7689;
+ public static final long REPORT_WATCHLIST_RECORDS_PERIOD_MILLIS =
+ TimeUnit.HOURS.toMillis(12);
+
+ @Override
+ public boolean onStartJob(final JobParameters jobParameters) {
+ if (jobParameters.getJobId() != REPORT_WATCHLIST_RECORDS_JOB_ID) {
+ return false;
+ }
+ if (DEBUG) Slog.d(TAG, "Start scheduled job.");
+ new NetworkWatchlistManager(this).reportWatchlistIfNecessary();
+ jobFinished(jobParameters, false);
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters jobParameters) {
+ return true; // Reschedule when possible.
+ }
+
+ /**
+ * Schedule the {@link ReportWatchlistJobService} to run periodically.
+ */
+ public static void schedule(Context context) {
+ if (DEBUG) Slog.d(TAG, "Scheduling records aggregator task");
+ final JobScheduler scheduler =
+ (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+ scheduler.schedule(new JobInfo.Builder(REPORT_WATCHLIST_RECORDS_JOB_ID,
+ new ComponentName(context, ReportWatchlistJobService.class))
+ //.setOverrideDeadline(45 * 1000) // Schedule job soon, for testing.
+ .setPeriodic(REPORT_WATCHLIST_RECORDS_PERIOD_MILLIS)
+ .setRequiresDeviceIdle(true)
+ .setRequiresBatteryNotLow(true)
+ .setPersisted(false)
+ .build());
+ }
+
+}
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
new file mode 100644
index 0000000..2247558
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2017 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.net.watchlist;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
+import android.os.DropBoxManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A Handler class for network watchlist logging on a background thread.
+ */
+class WatchlistLoggingHandler extends Handler {
+
+ private static final String TAG = WatchlistLoggingHandler.class.getSimpleName();
+ private static final boolean DEBUG = NetworkWatchlistService.DEBUG;
+
+ @VisibleForTesting
+ static final int LOG_WATCHLIST_EVENT_MSG = 1;
+ @VisibleForTesting
+ static final int REPORT_RECORDS_IF_NECESSARY_MSG = 2;
+
+ private static final long ONE_DAY_MS = TimeUnit.DAYS.toMillis(1);
+ private static final String DROPBOX_TAG = "network_watchlist_report";
+
+ private final Context mContext;
+ private final ContentResolver mResolver;
+ private final PackageManager mPm;
+ private final WatchlistReportDbHelper mDbHelper;
+ private final WatchlistSettings mSettings;
+ // A cache for uid and apk digest mapping.
+ // As uid won't be reused until reboot, it's safe to assume uid is unique per signature and app.
+ // TODO: Use more efficient data structure.
+ private final HashMap<Integer, byte[]> mCachedUidDigestMap = new HashMap<>();
+
+ private interface WatchlistEventKeys {
+ String HOST = "host";
+ String IP_ADDRESSES = "ipAddresses";
+ String UID = "uid";
+ String TIMESTAMP = "timestamp";
+ }
+
+ WatchlistLoggingHandler(Context context, Looper looper) {
+ super(looper);
+ mContext = context;
+ mPm = mContext.getPackageManager();
+ mResolver = mContext.getContentResolver();
+ mDbHelper = WatchlistReportDbHelper.getInstance(context);
+ mSettings = WatchlistSettings.getInstance();
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case LOG_WATCHLIST_EVENT_MSG: {
+ final Bundle data = msg.getData();
+ handleNetworkEvent(
+ data.getString(WatchlistEventKeys.HOST),
+ data.getStringArray(WatchlistEventKeys.IP_ADDRESSES),
+ data.getInt(WatchlistEventKeys.UID),
+ data.getLong(WatchlistEventKeys.TIMESTAMP)
+ );
+ break;
+ }
+ case REPORT_RECORDS_IF_NECESSARY_MSG:
+ tryAggregateRecords();
+ break;
+ default: {
+ Slog.d(TAG, "WatchlistLoggingHandler received an unknown of message.");
+ break;
+ }
+ }
+ }
+
+ /**
+ * Report network watchlist records if we collected enough data.
+ */
+ public void reportWatchlistIfNecessary() {
+ final Message msg = obtainMessage(REPORT_RECORDS_IF_NECESSARY_MSG);
+ sendMessage(msg);
+ }
+
+ /**
+ * Insert network traffic event to watchlist async queue processor.
+ */
+ public void asyncNetworkEvent(String host, String[] ipAddresses, int uid) {
+ final Message msg = obtainMessage(LOG_WATCHLIST_EVENT_MSG);
+ final Bundle bundle = new Bundle();
+ bundle.putString(WatchlistEventKeys.HOST, host);
+ bundle.putStringArray(WatchlistEventKeys.IP_ADDRESSES, ipAddresses);
+ bundle.putInt(WatchlistEventKeys.UID, uid);
+ bundle.putLong(WatchlistEventKeys.TIMESTAMP, System.currentTimeMillis());
+ msg.setData(bundle);
+ sendMessage(msg);
+ }
+
+ private void handleNetworkEvent(String hostname, String[] ipAddresses,
+ int uid, long timestamp) {
+ if (DEBUG) {
+ Slog.i(TAG, "handleNetworkEvent with host: " + hostname + ", uid: " + uid);
+ }
+ final String cncDomain = searchAllSubDomainsInWatchlist(hostname);
+ if (cncDomain != null) {
+ insertRecord(getDigestFromUid(uid), cncDomain, timestamp);
+ } else {
+ final String cncIp = searchIpInWatchlist(ipAddresses);
+ if (cncIp != null) {
+ insertRecord(getDigestFromUid(uid), cncIp, timestamp);
+ }
+ }
+ }
+
+ private boolean insertRecord(byte[] digest, String cncHost, long timestamp) {
+ final boolean result = mDbHelper.insertNewRecord(digest, cncHost, timestamp);
+ tryAggregateRecords();
+ return result;
+ }
+
+ private boolean shouldReportNetworkWatchlist() {
+ final long lastReportTime = Settings.Global.getLong(mResolver,
+ Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME, 0L);
+ final long currentTimestamp = System.currentTimeMillis();
+ if (currentTimestamp < lastReportTime) {
+ Slog.i(TAG, "Last report time is larger than current time, reset report");
+ mDbHelper.cleanup();
+ return false;
+ }
+ return currentTimestamp >= lastReportTime + ONE_DAY_MS;
+ }
+
+ private void tryAggregateRecords() {
+ if (shouldReportNetworkWatchlist()) {
+ Slog.i(TAG, "Start aggregating watchlist records.");
+ final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);
+ if (dbox != null && !dbox.isTagEnabled(DROPBOX_TAG)) {
+ final WatchlistReportDbHelper.AggregatedResult aggregatedResult =
+ mDbHelper.getAggregatedRecords();
+ final byte[] encodedResult = encodeAggregatedResult(aggregatedResult);
+ if (encodedResult != null) {
+ addEncodedReportToDropBox(encodedResult);
+ }
+ }
+ mDbHelper.cleanup();
+ Settings.Global.putLong(mResolver, Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME,
+ System.currentTimeMillis());
+ } else {
+ Slog.i(TAG, "No need to aggregate record yet.");
+ }
+ }
+
+ private byte[] encodeAggregatedResult(
+ WatchlistReportDbHelper.AggregatedResult aggregatedResult) {
+ // TODO: Encode results using differential privacy.
+ return null;
+ }
+
+ private void addEncodedReportToDropBox(byte[] encodedReport) {
+ final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);
+ dbox.addData(DROPBOX_TAG, encodedReport, 0);
+ }
+
+ /**
+ * Get app digest from app uid.
+ */
+ private byte[] getDigestFromUid(int uid) {
+ final byte[] cachedDigest = mCachedUidDigestMap.get(uid);
+ if (cachedDigest != null) {
+ return cachedDigest;
+ }
+ final String[] packageNames = mPm.getPackagesForUid(uid);
+ final int userId = UserHandle.getUserId(uid);
+ if (!ArrayUtils.isEmpty(packageNames)) {
+ for (String packageName : packageNames) {
+ try {
+ final String apkPath = mPm.getPackageInfoAsUser(packageName,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId)
+ .applicationInfo.publicSourceDir;
+ if (TextUtils.isEmpty(apkPath)) {
+ Slog.w(TAG, "Cannot find apkPath for " + packageName);
+ continue;
+ }
+ final byte[] digest = DigestUtils.getSha256Hash(new File(apkPath));
+ mCachedUidDigestMap.put(uid, digest);
+ return digest;
+ } catch (NameNotFoundException | NoSuchAlgorithmException | IOException e) {
+ Slog.e(TAG, "Should not happen", e);
+ return null;
+ }
+ }
+ } else {
+ Slog.e(TAG, "Should not happen");
+ }
+ return null;
+ }
+
+ /**
+ * Search if any ip addresses are in watchlist.
+ *
+ * @param ipAddresses Ip address that you want to search in watchlist.
+ * @return Ip address that exists in watchlist, null if it does not match anything.
+ */
+ private String searchIpInWatchlist(String[] ipAddresses) {
+ for (String ipAddress : ipAddresses) {
+ if (isIpInWatchlist(ipAddress)) {
+ return ipAddress;
+ }
+ }
+ return null;
+ }
+
+ /** Search if the ip is in watchlist */
+ private boolean isIpInWatchlist(String ipAddr) {
+ if (ipAddr == null) {
+ return false;
+ }
+ return mSettings.containsIp(ipAddr);
+ }
+
+ /** Search if the host is in watchlist */
+ private boolean isHostInWatchlist(String host) {
+ if (host == null) {
+ return false;
+ }
+ return mSettings.containsDomain(host);
+ }
+
+ /**
+ * Search if any sub-domain in host is in watchlist.
+ *
+ * @param host Host that we want to search.
+ * @return Domain that exists in watchlist, null if it does not match anything.
+ */
+ private String searchAllSubDomainsInWatchlist(String host) {
+ if (host == null) {
+ return null;
+ }
+ final String[] subDomains = getAllSubDomains(host);
+ for (String subDomain : subDomains) {
+ if (isHostInWatchlist(subDomain)) {
+ return subDomain;
+ }
+ }
+ return null;
+ }
+
+ /** Get all sub-domains in a host */
+ @VisibleForTesting
+ static String[] getAllSubDomains(String host) {
+ if (host == null) {
+ return null;
+ }
+ final ArrayList<String> subDomainList = new ArrayList<>();
+ subDomainList.add(host);
+ int index = host.indexOf(".");
+ while (index != -1) {
+ host = host.substring(index + 1);
+ if (!TextUtils.isEmpty(host)) {
+ subDomainList.add(host);
+ }
+ index = host.indexOf(".");
+ }
+ return subDomainList.toArray(new String[0]);
+ }
+}
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
new file mode 100644
index 0000000..f48463f
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2017 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.net.watchlist;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Pair;
+
+import com.android.internal.util.HexDump;
+
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Helper class to process watchlist read / save watchlist reports.
+ */
+class WatchlistReportDbHelper extends SQLiteOpenHelper {
+
+ private static final String TAG = "WatchlistReportDbHelper";
+
+ private static final String NAME = "watchlist_report.db";
+ private static final int VERSION = 2;
+
+ private static final int IDLE_CONNECTION_TIMEOUT_MS = 30000;
+
+ private static class WhiteListReportContract {
+ private static final String TABLE = "records";
+ private static final String APP_DIGEST = "app_digest";
+ private static final String CNC_DOMAIN = "cnc_domain";
+ private static final String TIMESTAMP = "timestamp";
+ }
+
+ private static final String CREATE_TABLE_MODEL = "CREATE TABLE "
+ + WhiteListReportContract.TABLE + "("
+ + WhiteListReportContract.APP_DIGEST + " BLOB,"
+ + WhiteListReportContract.CNC_DOMAIN + " TEXT,"
+ + WhiteListReportContract.TIMESTAMP + " INTEGER DEFAULT 0" + " )";
+
+ private static final int INDEX_DIGEST = 0;
+ private static final int INDEX_CNC_DOMAIN = 1;
+ private static final int INDEX_TIMESTAMP = 2;
+
+ private static final String[] DIGEST_DOMAIN_PROJECTION =
+ new String[] {
+ WhiteListReportContract.APP_DIGEST,
+ WhiteListReportContract.CNC_DOMAIN
+ };
+
+ private static WatchlistReportDbHelper sInstance;
+
+ /**
+ * Aggregated watchlist records.
+ */
+ public static class AggregatedResult {
+ // A list of digests that visited c&c domain or ip before.
+ Set<String> appDigestList;
+
+ // The c&c domain or ip visited before.
+ String cncDomainVisited;
+
+ // A list of app digests and c&c domain visited.
+ HashMap<String, String> appDigestCNCList;
+ }
+
+ private WatchlistReportDbHelper(Context context) {
+ super(context, WatchlistSettings.getSystemWatchlistFile(NAME).getAbsolutePath(),
+ null, VERSION);
+ // Memory optimization - close idle connections after 30s of inactivity
+ setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
+ }
+
+ public static synchronized WatchlistReportDbHelper getInstance(Context context) {
+ if (sInstance != null) {
+ return sInstance;
+ }
+ sInstance = new WatchlistReportDbHelper(context);
+ return sInstance;
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL(CREATE_TABLE_MODEL);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // TODO: For now, drop older tables and recreate new ones.
+ db.execSQL("DROP TABLE IF EXISTS " + WhiteListReportContract.TABLE);
+ onCreate(db);
+ }
+
+ /**
+ * Insert new watchlist record.
+ *
+ * @param appDigest The digest of an app.
+ * @param cncDomain C&C domain that app visited.
+ * @return True if success.
+ */
+ public boolean insertNewRecord(byte[] appDigest, String cncDomain,
+ long timestamp) {
+ final SQLiteDatabase db = getWritableDatabase();
+ final ContentValues values = new ContentValues();
+ values.put(WhiteListReportContract.APP_DIGEST, appDigest);
+ values.put(WhiteListReportContract.CNC_DOMAIN, cncDomain);
+ values.put(WhiteListReportContract.TIMESTAMP, timestamp);
+ return db.insert(WhiteListReportContract.TABLE, null, values) != -1;
+ }
+
+ /**
+ * Aggregate the records in database, and return a rappor encoded result.
+ */
+ public AggregatedResult getAggregatedRecords() {
+ final long twoDaysBefore = getTwoDaysBeforeTimestamp();
+ final long yesterday = getYesterdayTimestamp();
+ final String selectStatement = WhiteListReportContract.TIMESTAMP + " >= ? AND " +
+ WhiteListReportContract.TIMESTAMP + " <= ?";
+
+ final SQLiteDatabase db = getReadableDatabase();
+ Cursor c = null;
+ try {
+ c = db.query(true /* distinct */,
+ WhiteListReportContract.TABLE, DIGEST_DOMAIN_PROJECTION, selectStatement,
+ new String[]{"" + twoDaysBefore, "" + yesterday}, null, null,
+ null, null);
+ if (c == null || c.getCount() == 0) {
+ return null;
+ }
+ final AggregatedResult result = new AggregatedResult();
+ result.cncDomainVisited = null;
+ // After aggregation, each digest maximum will have only 1 record.
+ result.appDigestList = new HashSet<>();
+ result.appDigestCNCList = new HashMap<>();
+ while (c.moveToNext()) {
+ // We use hex string here as byte[] cannot be a key in HashMap.
+ String digestHexStr = HexDump.toHexString(c.getBlob(INDEX_DIGEST));
+ String cncDomain = c.getString(INDEX_CNC_DOMAIN);
+
+ result.appDigestList.add(digestHexStr);
+ if (result.cncDomainVisited != null) {
+ result.cncDomainVisited = cncDomain;
+ }
+ result.appDigestCNCList.put(digestHexStr, cncDomain);
+ }
+ return result;
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ }
+
+ /**
+ * Remove all the records before yesterday.
+ *
+ * @return True if success.
+ */
+ public boolean cleanup() {
+ final SQLiteDatabase db = getWritableDatabase();
+ final long twoDaysBefore = getTwoDaysBeforeTimestamp();
+ final String clause = WhiteListReportContract.TIMESTAMP + "< " + twoDaysBefore;
+ return db.delete(WhiteListReportContract.TABLE, clause, null) != 0;
+ }
+
+ static long getTwoDaysBeforeTimestamp() {
+ return getMidnightTimestamp(2);
+ }
+
+ static long getYesterdayTimestamp() {
+ return getMidnightTimestamp(1);
+ }
+
+ static long getMidnightTimestamp(int daysBefore) {
+ java.util.Calendar date = new GregorianCalendar();
+ // reset hour, minutes, seconds and millis
+ date.set(java.util.Calendar.HOUR_OF_DAY, 0);
+ date.set(java.util.Calendar.MINUTE, 0);
+ date.set(java.util.Calendar.SECOND, 0);
+ date.set(java.util.Calendar.MILLISECOND, 0);
+ date.add(java.util.Calendar.DAY_OF_MONTH, -daysBefore);
+ return date.getTimeInMillis();
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
new file mode 100644
index 0000000..c50f0d5
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2017 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.net.watchlist;
+
+import android.os.Environment;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.HexDump;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.zip.CRC32;
+
+/**
+ * A util class to do watchlist settings operations, like setting watchlist, query if a domain
+ * exists in watchlist.
+ */
+class WatchlistSettings {
+ private static final String TAG = "WatchlistSettings";
+
+ // Settings xml will be stored in /data/system/network_watchlist/watchlist_settings.xml
+ static final String SYSTEM_WATCHLIST_DIR = "network_watchlist";
+
+ private static final String WATCHLIST_XML_FILE = "watchlist_settings.xml";
+
+ private static class XmlTags {
+ private static final String WATCHLIST_SETTINGS = "watchlist-settings";
+ private static final String SHA256_DOMAIN = "sha256-domain";
+ private static final String CRC32_DOMAIN = "crc32-domain";
+ private static final String SHA256_IP = "sha256-ip";
+ private static final String CRC32_IP = "crc32-ip";
+ private static final String HASH = "hash";
+ }
+
+ private static WatchlistSettings sInstance = new WatchlistSettings();
+ private final AtomicFile mXmlFile;
+ private final Object mLock = new Object();
+ private HarmfulDigests mCrc32DomainDigests = new HarmfulDigests(new ArrayList<>());
+ private HarmfulDigests mSha256DomainDigests = new HarmfulDigests(new ArrayList<>());
+ private HarmfulDigests mCrc32IpDigests = new HarmfulDigests(new ArrayList<>());
+ private HarmfulDigests mSha256IpDigests = new HarmfulDigests(new ArrayList<>());
+
+ public static synchronized WatchlistSettings getInstance() {
+ return sInstance;
+ }
+
+ private WatchlistSettings() {
+ this(getSystemWatchlistFile(WATCHLIST_XML_FILE));
+ }
+
+ @VisibleForTesting
+ protected WatchlistSettings(File xmlFile) {
+ mXmlFile = new AtomicFile(xmlFile);
+ readSettingsLocked();
+ }
+
+ static File getSystemWatchlistFile(String filename) {
+ final File dataSystemDir = Environment.getDataSystemDirectory();
+ final File systemWatchlistDir = new File(dataSystemDir, SYSTEM_WATCHLIST_DIR);
+ systemWatchlistDir.mkdirs();
+ return new File(systemWatchlistDir, filename);
+ }
+
+ private void readSettingsLocked() {
+ synchronized (mLock) {
+ FileInputStream stream;
+ try {
+ stream = mXmlFile.openRead();
+ } catch (FileNotFoundException e) {
+ Log.i(TAG, "No watchlist settings: " + mXmlFile.getBaseFile().getAbsolutePath());
+ return;
+ }
+
+ final List<byte[]> crc32DomainList = new ArrayList<>();
+ final List<byte[]> sha256DomainList = new ArrayList<>();
+ final List<byte[]> crc32IpList = new ArrayList<>();
+ final List<byte[]> sha256IpList = new ArrayList<>();
+
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, StandardCharsets.UTF_8.name());
+ parser.nextTag();
+ parser.require(XmlPullParser.START_TAG, null, XmlTags.WATCHLIST_SETTINGS);
+ while (parser.nextTag() == XmlPullParser.START_TAG) {
+ String tagName = parser.getName();
+ switch (tagName) {
+ case XmlTags.CRC32_DOMAIN:
+ parseHash(parser, tagName, crc32DomainList);
+ break;
+ case XmlTags.CRC32_IP:
+ parseHash(parser, tagName, crc32IpList);
+ break;
+ case XmlTags.SHA256_DOMAIN:
+ parseHash(parser, tagName, sha256DomainList);
+ break;
+ case XmlTags.SHA256_IP:
+ parseHash(parser, tagName, sha256IpList);
+ break;
+ default:
+ Log.w(TAG, "Unknown element: " + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ parser.require(XmlPullParser.END_TAG, null, XmlTags.WATCHLIST_SETTINGS);
+ writeSettingsToMemory(crc32DomainList, sha256DomainList, crc32IpList, sha256IpList);
+ } catch (IllegalStateException | NullPointerException | NumberFormatException |
+ XmlPullParserException | IOException | IndexOutOfBoundsException e) {
+ Log.w(TAG, "Failed parsing " + e);
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ private void parseHash(XmlPullParser parser, String tagName, List<byte[]> hashSet)
+ throws IOException, XmlPullParserException {
+ parser.require(XmlPullParser.START_TAG, null, tagName);
+ while (parser.nextTag() == XmlPullParser.START_TAG) {
+ parser.require(XmlPullParser.START_TAG, null, XmlTags.HASH);
+ byte[] hash = HexDump.hexStringToByteArray(parser.nextText());
+ parser.require(XmlPullParser.END_TAG, null, XmlTags.HASH);
+ hashSet.add(hash);
+ }
+ parser.require(XmlPullParser.END_TAG, null, tagName);
+ }
+
+ /**
+ * Write network watchlist settings to disk.
+ * Adb should not use it, should use writeSettingsToMemory directly instead.
+ */
+ public void writeSettingsToDisk(List<byte[]> newCrc32DomainList,
+ List<byte[]> newSha256DomainList,
+ List<byte[]> newCrc32IpList,
+ List<byte[]> newSha256IpList) {
+ synchronized (mLock) {
+ FileOutputStream stream;
+ try {
+ stream = mXmlFile.startWrite();
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to write display settings: " + e);
+ return;
+ }
+
+ try {
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, StandardCharsets.UTF_8.name());
+ out.startDocument(null, true);
+ out.startTag(null, XmlTags.WATCHLIST_SETTINGS);
+
+ writeHashSetToXml(out, XmlTags.SHA256_DOMAIN, newSha256DomainList);
+ writeHashSetToXml(out, XmlTags.SHA256_IP, newSha256IpList);
+ writeHashSetToXml(out, XmlTags.CRC32_DOMAIN, newCrc32DomainList);
+ writeHashSetToXml(out, XmlTags.CRC32_IP, newCrc32IpList);
+
+ out.endTag(null, XmlTags.WATCHLIST_SETTINGS);
+ out.endDocument();
+ mXmlFile.finishWrite(stream);
+ writeSettingsToMemory(newCrc32DomainList, newSha256DomainList, newCrc32IpList,
+ newSha256IpList);
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to write display settings, restoring backup.", e);
+ mXmlFile.failWrite(stream);
+ }
+ }
+ }
+
+ /**
+ * Write network watchlist settings to memory.
+ */
+ public void writeSettingsToMemory(List<byte[]> newCrc32DomainList,
+ List<byte[]> newSha256DomainList,
+ List<byte[]> newCrc32IpList,
+ List<byte[]> newSha256IpList) {
+ synchronized (mLock) {
+ mCrc32DomainDigests = new HarmfulDigests(newCrc32DomainList);
+ mCrc32IpDigests = new HarmfulDigests(newCrc32IpList);
+ mSha256DomainDigests = new HarmfulDigests(newSha256DomainList);
+ mSha256IpDigests = new HarmfulDigests(newSha256IpList);
+ }
+ }
+
+ private static void writeHashSetToXml(XmlSerializer out, String tagName, List<byte[]> hashSet)
+ throws IOException {
+ out.startTag(null, tagName);
+ for (byte[] hash : hashSet) {
+ out.startTag(null, XmlTags.HASH);
+ out.text(HexDump.toHexString(hash));
+ out.endTag(null, XmlTags.HASH);
+ }
+ out.endTag(null, tagName);
+ }
+
+ public boolean containsDomain(String domain) {
+ // First it does a quick CRC32 check.
+ final byte[] crc32 = getCrc32(domain);
+ if (!mCrc32DomainDigests.contains(crc32)) {
+ return false;
+ }
+ // Now we do a slow SHA256 check.
+ final byte[] sha256 = getSha256(domain);
+ return mSha256DomainDigests.contains(sha256);
+ }
+
+ public boolean containsIp(String ip) {
+ // First it does a quick CRC32 check.
+ final byte[] crc32 = getCrc32(ip);
+ if (!mCrc32IpDigests.contains(crc32)) {
+ return false;
+ }
+ // Now we do a slow SHA256 check.
+ final byte[] sha256 = getSha256(ip);
+ return mSha256IpDigests.contains(sha256);
+ }
+
+
+ /** Get CRC32 of a string */
+ private byte[] getCrc32(String str) {
+ final CRC32 crc = new CRC32();
+ crc.update(str.getBytes());
+ final long tmp = crc.getValue();
+ return new byte[]{(byte)(tmp >> 24 & 255), (byte)(tmp >> 16 & 255),
+ (byte)(tmp >> 8 & 255), (byte)(tmp & 255)};
+ }
+
+ /** Get SHA256 of a string */
+ private byte[] getSha256(String str) {
+ MessageDigest messageDigest;
+ try {
+ messageDigest = MessageDigest.getInstance("SHA256");
+ } catch (NoSuchAlgorithmException e) {
+ /* can't happen */
+ return null;
+ }
+ messageDigest.update(str.getBytes());
+ return messageDigest.digest();
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("Domain CRC32 digest list:");
+ mCrc32DomainDigests.dump(fd, pw, args);
+ pw.println("Domain SHA256 digest list:");
+ mSha256DomainDigests.dump(fd, pw, args);
+ pw.println("Ip CRC32 digest list:");
+ mCrc32IpDigests.dump(fd, pw, args);
+ pw.println("Ip SHA256 digest list:");
+ mSha256IpDigests.dump(fd, pw, args);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index cf9e6f3..19b0d9b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -52,16 +52,7 @@
// Load the property for the given reason and check for validity. This will throw an
// exception in case the reason or value are invalid.
private static String getAndCheckValidity(int reason) {
- String sysPropName = getSystemPropertyName(reason);
- String sysPropValue;
- // TODO: This is a temporary hack to keep marlin booting on aosp/master while we
- // figure out how to deal with these system properties that currently appear on
- // vendor.
- if ("pm.dexopt.inactive".equals(sysPropName)) {
- sysPropValue = "verify";
- } else {
- sysPropValue = SystemProperties.get(sysPropName);
- }
+ String sysPropValue = SystemProperties.get(getSystemPropertyName(reason));
if (sysPropValue == null || sysPropValue.isEmpty() ||
!DexFile.isValidCompilerFilter(sysPropValue)) {
throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 011e1ac..4b13404 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -66,6 +66,7 @@
public static final Set<String> USER_RESTRICTIONS = newSetWithUniqueCheck(new String[] {
UserManager.DISALLOW_CONFIG_WIFI,
+ UserManager.DISALLOW_CONFIG_LOCALE,
UserManager.DISALLOW_MODIFY_ACCOUNTS,
UserManager.DISALLOW_INSTALL_APPS,
UserManager.DISALLOW_UNINSTALL_APPS,
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7837940..caf85b0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -8084,22 +8084,28 @@
private int updateLightNavigationBarLw(int vis, WindowState opaque,
WindowState opaqueOrDimming) {
final WindowState imeWin = mWindowManagerFuncs.getInputMethodWindowLw();
-
- final WindowState navColorWin;
- if (imeWin != null && imeWin.isVisibleLw() && mNavigationBarPosition == NAV_BAR_BOTTOM) {
- navColorWin = imeWin;
- } else {
- navColorWin = opaqueOrDimming;
+ final boolean isImeShownWithBottomNavBar =
+ imeWin != null && imeWin.isVisibleLw() && mNavigationBarPosition == NAV_BAR_BOTTOM;
+ if (isImeShownWithBottomNavBar) {
+ final int winFlags = PolicyControl.getWindowFlags(imeWin, null);
+ if ((winFlags & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
+ // If the IME window is visible and explicitly requesting custom nav bar background
+ // rendering, respect its light flag.
+ vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+ vis |= PolicyControl.getSystemUiVisibility(imeWin, null)
+ & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+ return vis;
+ }
}
- if (navColorWin != null) {
- if (navColorWin == opaque) {
- // If the top fullscreen-or-dimming window is also the top fullscreen, respect
- // its light flag.
+ if (opaqueOrDimming != null) {
+ if (opaqueOrDimming == opaque) {
+ // If the top fullscreen-or-dimming window is also the top fullscreen window,
+ // respect its light flag.
vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- vis |= PolicyControl.getSystemUiVisibility(navColorWin, null)
+ vis |= PolicyControl.getSystemUiVisibility(opaqueOrDimming, null)
& View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- } else if (navColorWin.isDimming() || navColorWin == imeWin) {
+ } else if (opaqueOrDimming.isDimming() || isImeShownWithBottomNavBar) {
// Otherwise if it's dimming or it's the IME window, clear the light flag.
vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
}
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 273b945..0c73fe8 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -56,6 +56,9 @@
private static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor";
private static final String KEY_FULLBACKUP_DEFERRED = "fullbackup_deferred";
private static final String KEY_KEYVALUE_DEFERRED = "keyvaluebackup_deferred";
+ private static final String KEY_FORCE_ALL_APPS_STANDBY_JOBS = "force_all_apps_standby_jobs";
+ private static final String KEY_FORCE_ALL_APPS_STANDBY_ALARMS = "force_all_apps_standby_alarms";
+ private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled";
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -141,6 +144,21 @@
*/
private float mAdjustBrightnessFactor;
+ /**
+ * Whether to put all apps in the stand-by mode or not for job scheduler.
+ */
+ private boolean mForceAllAppsStandbyJobs;
+
+ /**
+ * Whether to put all apps in the stand-by mode or not for alarms.
+ */
+ private boolean mForceAllAppsStandbyAlarms;
+
+ /**
+ * Weather to show non-essential sensors (e.g. edge sensors) or not.
+ */
+ private boolean mOptionalSensorsDisabled;
+
private ContentResolver mContentResolver;
public BatterySaverPolicy(Handler handler) {
@@ -180,10 +198,14 @@
mAdjustBrightnessDisabled = mParser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, false);
mAdjustBrightnessFactor = mParser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, 0.5f);
mDataSaverDisabled = mParser.getBoolean(KEY_DATASAVER_DISABLED, true);
+ mForceAllAppsStandbyJobs = mParser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_JOBS, true);
+ mForceAllAppsStandbyAlarms =
+ mParser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_ALARMS, true);
+ mOptionalSensorsDisabled = mParser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true);
// Get default value from Settings.Secure
final int defaultGpsMode = Settings.Secure.getInt(mContentResolver, SECURE_KEY_GPS_MODE,
- GPS_MODE_DISABLED_WHEN_SCREEN_OFF);
+ GPS_MODE_NO_CHANGE);
mGpsMode = mParser.getInt(KEY_GPS_MODE, defaultGpsMode);
}
}
@@ -235,6 +257,15 @@
case ServiceType.VIBRATION:
return builder.setBatterySaverEnabled(mVibrationDisabled)
.build();
+ case ServiceType.FORCE_ALL_APPS_STANDBY_JOBS:
+ return builder.setBatterySaverEnabled(mForceAllAppsStandbyJobs)
+ .build();
+ case ServiceType.FORCE_ALL_APPS_STANDBY_ALARMS:
+ return builder.setBatterySaverEnabled(mForceAllAppsStandbyAlarms)
+ .build();
+ case ServiceType.OPTIONAL_SENSORS:
+ return builder.setBatterySaverEnabled(mOptionalSensorsDisabled)
+ .build();
default:
return builder.setBatterySaverEnabled(realMode)
.build();
@@ -259,6 +290,8 @@
pw.println(" " + KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + mAdjustBrightnessDisabled);
pw.println(" " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + mAdjustBrightnessFactor);
pw.println(" " + KEY_GPS_MODE + "=" + mGpsMode);
-
+ pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY_JOBS + "=" + mForceAllAppsStandbyJobs);
+ pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY_ALARMS + "=" + mForceAllAppsStandbyAlarms);
+ pw.println(" " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + mOptionalSensorsDisabled);
}
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 416a606..a153fdf 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -71,6 +71,8 @@
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.WindowManagerPolicy;
+import android.widget.Toast;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
@@ -1022,6 +1024,13 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
Manifest.permission.DEVICE_POWER);
+
+ // STOPSHIP Remove the toast.
+ if (mLowPowerModeEnabled) {
+ Toast.makeText(mContext,
+ com.android.internal.R.string.battery_saver_warning,
+ Toast.LENGTH_LONG).show();
+ }
}
});
}
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 76e25ba..4567e10 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -21,6 +21,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.NonNull;
import android.content.ClipData;
import android.graphics.PixelFormat;
import android.os.Binder;
@@ -35,6 +36,8 @@
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
+import android.view.WindowManagerInternal.IDragDropCallback;
+import com.android.internal.util.Preconditions;
import com.android.server.input.InputWindowHandle;
/**
@@ -61,6 +64,7 @@
private WindowManagerService mService;
private final Handler mHandler;
+
/**
* Lock to preserve the order of state updates.
* The lock is used to process drag and drop state updates in order without having the window
@@ -94,6 +98,11 @@
*/
private final Object mWriteLock = new Object();
+ /**
+ * Callback which is used to sync drag state with the vendor-specific code.
+ */
+ @NonNull private IDragDropCallback mCallback = new IDragDropCallback() {};
+
boolean dragDropActiveLocked() {
return mDragState != null;
}
@@ -102,6 +111,13 @@
return mDragState.getInputWindowHandle();
}
+ void registerCallback(IDragDropCallback callback) {
+ Preconditions.checkNotNull(callback);
+ synchronized (mWriteLock) {
+ mCallback = callback;
+ }
+ }
+
DragDropController(WindowManagerService service, Looper looper) {
mService = service;
mHandler = new DragHandler(service, looper);
@@ -169,6 +185,10 @@
}
synchronized (mWriteLock) {
+ if (!mCallback.performDrag(window, dragToken, touchSource, touchX, touchY, thumbCenterX,
+ thumbCenterY, data)) {
+ return false;
+ }
synchronized (mService.mWindowMap) {
if (mDragState == null) {
Slog.w(TAG_WM, "No drag prepared");
@@ -251,6 +271,7 @@
}
synchronized (mWriteLock) {
+ mCallback.reportDropResult(window, consumed);
synchronized (mService.mWindowMap) {
if (mDragState == null) {
// Most likely the drop recipient ANRed and we ended the drag
@@ -288,6 +309,7 @@
}
synchronized (mWriteLock) {
+ mCallback.cancelDragAndDrop(dragToken);
synchronized (mService.mWindowMap) {
if (mDragState == null) {
Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 6e89e3e..170feac 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -603,6 +603,14 @@
} else {
maxPosition = computeMaxPosition(maxPosition);
}
+
+ // preserve POSITION_BOTTOM/POSITION_TOP positions if they are still valid.
+ if (targetPosition == POSITION_BOTTOM && minPosition == 0) {
+ return POSITION_BOTTOM;
+ } else if (targetPosition == POSITION_TOP
+ && maxPosition == (addingNew ? stackSize : stackSize - 1)) {
+ return POSITION_TOP;
+ }
// Reset position based on minimum/maximum possible positions.
return Math.min(Math.max(targetPosition, minPosition), maxPosition);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d9ba7d5..35870b1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -125,6 +125,7 @@
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.content.BroadcastReceiver;
+import android.content.ClipData;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -7441,6 +7442,11 @@
mVr2dDisplayId = vr2dDisplayId;
}
}
+
+ @Override
+ public void registerDragDropControllerCallback(IDragDropCallback callback) {
+ mDragDropController.registerCallback(callback);
+ }
}
void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
index 0085931..0aaf32c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
@@ -107,7 +107,8 @@
return false;
}
try {
- if (mIpConnectivityMetrics.registerNetdEventCallback(mNetdEventCallback)) {
+ if (mIpConnectivityMetrics.addNetdEventCallback(
+ INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY, mNetdEventCallback)) {
mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
/* allowIo */ false);
mHandlerThread.start();
@@ -138,7 +139,8 @@
// logging is forcefully disabled even if unregistering fails
return true;
}
- return mIpConnectivityMetrics.unregisterNetdEventCallback();
+ return mIpConnectivityMetrics.removeNetdEventCallback(
+ INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY);
} catch (RemoteException re) {
Slog.wtf(TAG, "Failed to make remote calls to unregister the callback", re);
return true;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f8bcb73..90df82a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -37,7 +37,6 @@
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
@@ -52,7 +51,7 @@
import android.view.WindowManager;
import com.android.internal.R;
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BinderInternal;
@@ -69,7 +68,7 @@
import com.android.server.coverage.CoverageService;
import com.android.server.devicepolicy.DevicePolicyManagerService;
import com.android.server.display.DisplayManagerService;
-import com.android.server.display.NightDisplayService;
+import com.android.server.display.ColorDisplayService;
import com.android.server.dreams.DreamManagerService;
import com.android.server.emergency.EmergencyAffordanceService;
import com.android.server.fingerprint.FingerprintService;
@@ -83,6 +82,7 @@
import com.android.server.media.projection.MediaProjectionManagerService;
import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.net.NetworkStatsService;
+import com.android.server.net.watchlist.NetworkWatchlistService;
import com.android.server.notification.NotificationManagerService;
import com.android.server.oemlock.OemLockService;
import com.android.server.om.OverlayManagerService;
@@ -884,6 +884,10 @@
mSystemServiceManager.startService(IpConnectivityMetrics.class);
traceEnd();
+ traceBeginAndSlog("NetworkWatchlistService");
+ mSystemServiceManager.startService(NetworkWatchlistService.Lifecycle.class);
+ traceEnd();
+
traceBeginAndSlog("PinnerService");
mSystemServiceManager.startService(PinnerService.class);
traceEnd();
@@ -1277,9 +1281,9 @@
mSystemServiceManager.startService(TwilightService.class);
traceEnd();
- if (NightDisplayController.isAvailable(context)) {
+ if (ColorDisplayController.isAvailable(context)) {
traceBeginAndSlog("StartNightDisplay");
- mSystemServiceManager.startService(NightDisplayService.class);
+ mSystemServiceManager.startService(ColorDisplayService.class);
traceEnd();
}
diff --git a/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_settings_test1.xml b/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_settings_test1.xml
new file mode 100644
index 0000000..bb97e94
--- /dev/null
+++ b/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_settings_test1.xml
@@ -0,0 +1,27 @@
+<?xml version='1.0'?>
+<watchlist-settings>
+ <sha256-domain>
+ <!-- test-cc-domain.com -->
+ <hash>8E7DCD2AEB4F364358242BB3F403263E61E3B4AECE4E2500FF28BF32E52FF0F1</hash>
+ <!-- test-cc-match-sha256-only.com -->
+ <hash>F0905DA7549614957B449034C281EF7BDEFDBC2B6E050AD1E78D6DE18FBD0D5F</hash>
+ </sha256-domain>
+ <sha256-ip>
+ <!-- 127.0.0.2 -->
+ <hash>1EDD62868F2767A1FFF68DF0A4CB3C23448E45100715768DB9310B5E719536A1</hash>
+ <!-- 127.0.0.3, match in sha256 only -->
+ <hash>18DD41C9F2E8E4879A1575FB780514EF33CF6E1F66578C4AE7CCA31F49B9F2ED</hash>
+ </sha256-ip>
+ <crc32-domain>
+ <!-- test-cc-domain.com -->
+ <hash>6C67059D</hash>
+ <!-- test-cc-match-crc32-only.com -->
+ <hash>3DC775F8</hash>
+ </crc32-domain>
+ <crc32-ip>
+ <!-- 127.0.0.2 -->
+ <hash>4EBEB612</hash>
+ <!-- 127.0.0.4, match in crc32 only -->
+ <hash>A7DD1327</hash>
+ </crc32-ip>
+</watchlist-settings>
diff --git a/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/ColorDisplayServiceTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java
rename to services/tests/servicestests/src/com/android/server/ColorDisplayServiceTest.java
index 3a92d63..46b364c 100644
--- a/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ColorDisplayServiceTest.java
@@ -29,10 +29,10 @@
import android.support.test.runner.AndroidJUnit4;
import android.test.mock.MockContentResolver;
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.display.DisplayTransformManager;
-import com.android.server.display.NightDisplayService;
+import com.android.server.display.ColorDisplayService;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
@@ -55,15 +55,15 @@
import static org.mockito.Mockito.doReturn;
@RunWith(AndroidJUnit4.class)
-public class NightDisplayServiceTest {
+public class ColorDisplayServiceTest {
private Context mContext;
private int mUserId;
private MockTwilightManager mTwilightManager;
- private NightDisplayController mNightDisplayController;
- private NightDisplayService mNightDisplayService;
+ private ColorDisplayController mColorDisplayController;
+ private ColorDisplayService mColorDisplayService;
@Before
public void setUp() {
@@ -85,8 +85,8 @@
mTwilightManager = new MockTwilightManager();
LocalServices.addService(TwilightManager.class, mTwilightManager);
- mNightDisplayController = new NightDisplayController(mContext, mUserId);
- mNightDisplayService = new NightDisplayService(mContext);
+ mColorDisplayController = new ColorDisplayController(mContext, mUserId);
+ mColorDisplayService = new ColorDisplayService(mContext);
}
@After
@@ -94,8 +94,8 @@
LocalServices.removeServiceForTest(DisplayTransformManager.class);
LocalServices.removeServiceForTest(TwilightManager.class);
- mNightDisplayService = null;
- mNightDisplayController = null;
+ mColorDisplayService = null;
+ mColorDisplayController = null;
mTwilightManager = null;
@@ -902,9 +902,9 @@
* @param endTimeOffset the offset relative to now to deactivate Night display (in minutes)
*/
private void setAutoModeCustom(int startTimeOffset, int endTimeOffset) {
- mNightDisplayController.setAutoMode(NightDisplayController.AUTO_MODE_CUSTOM);
- mNightDisplayController.setCustomStartTime(getLocalTimeRelativeToNow(startTimeOffset));
- mNightDisplayController.setCustomEndTime(getLocalTimeRelativeToNow(endTimeOffset));
+ mColorDisplayController.setAutoMode(ColorDisplayController.AUTO_MODE_CUSTOM);
+ mColorDisplayController.setCustomStartTime(getLocalTimeRelativeToNow(startTimeOffset));
+ mColorDisplayController.setCustomEndTime(getLocalTimeRelativeToNow(endTimeOffset));
}
/**
@@ -914,7 +914,7 @@
* @param sunriseOffset the offset relative to now for sunrise (in minutes)
*/
private void setAutoModeTwilight(int sunsetOffset, int sunriseOffset) {
- mNightDisplayController.setAutoMode(NightDisplayController.AUTO_MODE_TWILIGHT);
+ mColorDisplayController.setAutoMode(ColorDisplayController.AUTO_MODE_TWILIGHT);
mTwilightManager.setTwilightState(
getTwilightStateRelativeToNow(sunsetOffset, sunriseOffset));
}
@@ -927,7 +927,7 @@
* activated (in minutes)
*/
private void setActivated(boolean activated, int lastActivatedTimeOffset) {
- mNightDisplayController.setActivated(activated);
+ mColorDisplayController.setActivated(activated);
Secure.putStringForUser(mContext.getContentResolver(),
Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
LocalDateTime.now().plusMinutes(lastActivatedTimeOffset).toString(),
@@ -935,7 +935,7 @@
}
/**
- * Convenience method to start {@link #mNightDisplayService}.
+ * Convenience method to start {@link #mColorDisplayService}.
*/
private void startService() {
Secure.putIntForUser(mContext.getContentResolver(), Secure.USER_SETUP_COMPLETE, 1, mUserId);
@@ -943,9 +943,9 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
- mNightDisplayService.onStart();
- mNightDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
- mNightDisplayService.onStartUser(mUserId);
+ mColorDisplayService.onStart();
+ mColorDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ mColorDisplayService.onStartUser(mUserId);
}
});
}
@@ -957,7 +957,7 @@
*/
private void assertActivated(boolean activated) {
assertWithMessage("Invalid Night display activated state")
- .that(mNightDisplayController.isActivated())
+ .that(mColorDisplayController.isActivated())
.isEqualTo(activated);
}
@@ -988,21 +988,21 @@
final LocalDateTime now = LocalDateTime.now();
final ZoneId zoneId = ZoneId.systemDefault();
- long sunsetMillis = NightDisplayService.getDateTimeBefore(sunset, now)
+ long sunsetMillis = ColorDisplayService.getDateTimeBefore(sunset, now)
.atZone(zoneId)
.toInstant()
.toEpochMilli();
- long sunriseMillis = NightDisplayService.getDateTimeBefore(sunrise, now)
+ long sunriseMillis = ColorDisplayService.getDateTimeBefore(sunrise, now)
.atZone(zoneId)
.toInstant()
.toEpochMilli();
if (sunsetMillis < sunriseMillis) {
- sunsetMillis = NightDisplayService.getDateTimeAfter(sunset, now)
+ sunsetMillis = ColorDisplayService.getDateTimeAfter(sunset, now)
.atZone(zoneId)
.toInstant()
.toEpochMilli();
} else {
- sunriseMillis = NightDisplayService.getDateTimeAfter(sunrise, now)
+ sunriseMillis = ColorDisplayService.getDateTimeAfter(sunrise, now)
.atZone(zoneId)
.toInstant()
.toEpochMilli();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 9d23fe9..6de3395 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3193,7 +3193,7 @@
// setUp() adds a secondary user for CALLER_USER_HANDLE. Remove it as otherwise the
// feature is disabled because there are non-affiliated secondary users.
getServices().removeUser(DpmMockContext.CALLER_USER_HANDLE);
- when(getServices().iipConnectivityMetrics.registerNetdEventCallback(anyObject()))
+ when(getServices().iipConnectivityMetrics.addNetdEventCallback(anyInt(), anyObject()))
.thenReturn(true);
// No logs were retrieved so far.
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/HarmfulDigestsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/HarmfulDigestsTests.java
new file mode 100644
index 0000000..a34f95e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/HarmfulDigestsTests.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 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.net.watchlist;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.HexDump;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+/**
+ * runtest frameworks-services -c com.android.server.net.watchlist.HarmfulDigestsTests
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class HarmfulDigestsTests {
+
+ private static final byte[] TEST_DIGEST_1 = HexDump.hexStringToByteArray("AAAAAA");
+ private static final byte[] TEST_DIGEST_2 = HexDump.hexStringToByteArray("BBBBBB");
+ private static final byte[] TEST_DIGEST_3 = HexDump.hexStringToByteArray("AAAABB");
+ private static final byte[] TEST_DIGEST_4 = HexDump.hexStringToByteArray("BBBBAA");
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void testHarmfulDigests_setAndContains() throws Exception {
+ HarmfulDigests harmfulDigests = new HarmfulDigests(
+ Arrays.asList(new byte[][] {TEST_DIGEST_1}));
+ assertTrue(harmfulDigests.contains(TEST_DIGEST_1));
+ assertFalse(harmfulDigests.contains(TEST_DIGEST_2));
+ assertFalse(harmfulDigests.contains(TEST_DIGEST_3));
+ assertFalse(harmfulDigests.contains(TEST_DIGEST_4));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java
new file mode 100644
index 0000000..ccd3cdd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2017 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.net.watchlist;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.net.ConnectivityMetricsEvent;
+import android.net.IIpConnectivityMetrics;
+import android.net.INetdEventCallback;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.ServiceThread;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * runtest frameworks-services -c com.android.server.net.watchlist.NetworkWatchlistServiceTests
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class NetworkWatchlistServiceTests {
+
+ private static final long NETWOR_EVENT_TIMEOUT_SEC = 1;
+ private static final String TEST_HOST = "testhost.com";
+ private static final String TEST_IP = "7.6.8.9";
+ private static final String[] TEST_IPS =
+ new String[] {"1.2.3.4", "4.6.8.9", "2001:0db8:0001:0000:0000:0ab9:C0A8:0102"};
+
+ private static class TestHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case WatchlistLoggingHandler.LOG_WATCHLIST_EVENT_MSG:
+ onLogEvent();
+ break;
+ case WatchlistLoggingHandler.REPORT_RECORDS_IF_NECESSARY_MSG:
+ onAggregateEvent();
+ break;
+ default:
+ fail("Unexpected message: " + msg.what);
+ }
+ }
+
+ public void onLogEvent() {}
+ public void onAggregateEvent() {}
+ }
+
+ private static class TestIIpConnectivityMetrics implements IIpConnectivityMetrics {
+
+ int counter = 0;
+ INetdEventCallback callback = null;
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+
+ @Override
+ public int logEvent(ConnectivityMetricsEvent connectivityMetricsEvent)
+ throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public boolean addNetdEventCallback(int callerType, INetdEventCallback callback) {
+ counter++;
+ this.callback = callback;
+ return true;
+ }
+
+ @Override
+ public boolean removeNetdEventCallback(int callerType) {
+ counter--;
+ return true;
+ }
+ };
+
+ ServiceThread mHandlerThread;
+ WatchlistLoggingHandler mWatchlistHandler;
+ NetworkWatchlistService mWatchlistService;
+
+ @Before
+ public void setUp() {
+ mHandlerThread = new ServiceThread("NetworkWatchlistServiceTests",
+ Process.THREAD_PRIORITY_BACKGROUND, /* allowIo */ false);
+ mHandlerThread.start();
+ mWatchlistHandler = new WatchlistLoggingHandler(InstrumentationRegistry.getContext(),
+ mHandlerThread.getLooper());
+ mWatchlistService = new NetworkWatchlistService(InstrumentationRegistry.getContext(),
+ mHandlerThread, mWatchlistHandler, null);
+ }
+
+ @After
+ public void tearDown() {
+ mHandlerThread.quitSafely();
+ }
+
+ @Test
+ public void testStartStopWatchlistLogging() throws Exception {
+ TestIIpConnectivityMetrics connectivityMetrics = new TestIIpConnectivityMetrics() {
+ @Override
+ public boolean addNetdEventCallback(int callerType, INetdEventCallback callback) {
+ super.addNetdEventCallback(callerType, callback);
+ assertEquals(callerType, INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST);
+ return true;
+ }
+
+ @Override
+ public boolean removeNetdEventCallback(int callerType) {
+ super.removeNetdEventCallback(callerType);
+ assertEquals(callerType, INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST);
+ return true;
+ }
+ };
+ assertEquals(connectivityMetrics.counter, 0);
+ mWatchlistService.mIpConnectivityMetrics = connectivityMetrics;
+ assertTrue(mWatchlistService.startWatchlistLoggingImpl());
+ assertEquals(connectivityMetrics.counter, 1);
+ assertTrue(mWatchlistService.startWatchlistLoggingImpl());
+ assertEquals(connectivityMetrics.counter, 1);
+ assertTrue(mWatchlistService.stopWatchlistLoggingImpl());
+ assertEquals(connectivityMetrics.counter, 0);
+ assertTrue(mWatchlistService.stopWatchlistLoggingImpl());
+ assertEquals(connectivityMetrics.counter, 0);
+ assertTrue(mWatchlistService.startWatchlistLoggingImpl());
+ assertEquals(connectivityMetrics.counter, 1);
+ assertTrue(mWatchlistService.stopWatchlistLoggingImpl());
+ assertEquals(connectivityMetrics.counter, 0);
+ }
+
+ @Test
+ public void testNetworkEvents() throws Exception {
+ TestIIpConnectivityMetrics connectivityMetrics = new TestIIpConnectivityMetrics();
+ mWatchlistService.mIpConnectivityMetrics = connectivityMetrics;
+ assertTrue(mWatchlistService.startWatchlistLoggingImpl());
+
+ // Test DNS events
+ final CountDownLatch testDnsLatch = new CountDownLatch(1);
+ final Object[] dnsParams = new Object[3];
+ final WatchlistLoggingHandler testDnsHandler =
+ new WatchlistLoggingHandler(InstrumentationRegistry.getContext(),
+ mHandlerThread.getLooper()) {
+ @Override
+ public void asyncNetworkEvent(String host, String[] ipAddresses, int uid) {
+ dnsParams[0] = host;
+ dnsParams[1] = ipAddresses;
+ dnsParams[2] = uid;
+ testDnsLatch.countDown();
+ }
+ };
+ mWatchlistService.mNetworkWatchlistHandler = testDnsHandler;
+ connectivityMetrics.callback.onDnsEvent(TEST_HOST, TEST_IPS, TEST_IPS.length, 123L, 456);
+ if (!testDnsLatch.await(NETWOR_EVENT_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for network event");
+ }
+ assertEquals(TEST_HOST, dnsParams[0]);
+ for (int i = 0; i < TEST_IPS.length; i++) {
+ assertEquals(TEST_IPS[i], ((String[])dnsParams[1])[i]);
+ }
+ assertEquals(456, dnsParams[2]);
+
+ // Test connect events
+ final CountDownLatch testConnectLatch = new CountDownLatch(1);
+ final Object[] connectParams = new Object[3];
+ final WatchlistLoggingHandler testConnectHandler =
+ new WatchlistLoggingHandler(InstrumentationRegistry.getContext(),
+ mHandlerThread.getLooper()) {
+ @Override
+ public void asyncNetworkEvent(String host, String[] ipAddresses, int uid) {
+ connectParams[0] = host;
+ connectParams[1] = ipAddresses;
+ connectParams[2] = uid;
+ testConnectLatch.countDown();
+ }
+ };
+ mWatchlistService.mNetworkWatchlistHandler = testConnectHandler;
+ connectivityMetrics.callback.onConnectEvent(TEST_IP, 80, 123L, 456);
+ if (!testConnectLatch.await(NETWOR_EVENT_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for network event");
+ }
+ assertNull(connectParams[0]);
+ assertEquals(1, ((String[]) connectParams[1]).length);
+ assertEquals(TEST_IP, ((String[]) connectParams[1])[0]);
+ assertEquals(456, connectParams[2]);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java
new file mode 100644
index 0000000..e356b13
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 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.net.watchlist;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+/**
+ * runtest frameworks-services -c com.android.server.net.watchlist.WatchlistLoggingHandlerTests
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class WatchlistLoggingHandlerTests {
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void testWatchlistLoggingHandler_getAllSubDomains() throws Exception {
+ String[] subDomains = WatchlistLoggingHandler.getAllSubDomains("abc.def.gh.i.jkl.mm");
+ assertTrue(Arrays.equals(subDomains, new String[] {"abc.def.gh.i.jkl.mm",
+ "def.gh.i.jkl.mm", "gh.i.jkl.mm", "i.jkl.mm", "jkl.mm", "mm"}));
+ subDomains = WatchlistLoggingHandler.getAllSubDomains(null);
+ assertNull(subDomains);
+ subDomains = WatchlistLoggingHandler.getAllSubDomains("jkl.mm");
+ assertTrue(Arrays.equals(subDomains, new String[] {"jkl.mm", "mm"}));
+ subDomains = WatchlistLoggingHandler.getAllSubDomains("abc");
+ assertTrue(Arrays.equals(subDomains, new String[] {"abc"}));
+ subDomains = WatchlistLoggingHandler.getAllSubDomains("jkl.mm.");
+ assertTrue(Arrays.equals(subDomains, new String[] {"jkl.mm.", "mm."}));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java
new file mode 100644
index 0000000..f3cb980
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 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.net.watchlist;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.HexDump;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+
+/**
+ * runtest frameworks-services -c com.android.server.net.watchlist.WatchlistSettingsTests
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class WatchlistSettingsTests {
+
+ private static final String TEST_XML_1 = "NetworkWatchlistTest/watchlist_settings_test1.xml";
+ private static final String TEST_CC_DOMAIN = "test-cc-domain.com";
+ private static final String TEST_CC_IP = "127.0.0.2";
+ private static final String TEST_NOT_EXIST_CC_DOMAIN = "test-not-exist-cc-domain.com";
+ private static final String TEST_NOT_EXIST_CC_IP = "1.2.3.4";
+ private static final String TEST_SHA256_ONLY_DOMAIN = "test-cc-match-sha256-only.com";
+ private static final String TEST_SHA256_ONLY_IP = "127.0.0.3";
+ private static final String TEST_CRC32_ONLY_DOMAIN = "test-cc-match-crc32-only.com";
+ private static final String TEST_CRC32_ONLY_IP = "127.0.0.4";
+
+ private static final String TEST_NEW_CC_DOMAIN = "test-new-cc-domain.com";
+ private static final byte[] TEST_NEW_CC_DOMAIN_SHA256 = HexDump.hexStringToByteArray(
+ "B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43");
+ private static final byte[] TEST_NEW_CC_DOMAIN_CRC32 = HexDump.hexStringToByteArray("76795BD3");
+
+ private static final String TEST_NEW_CC_IP = "1.1.1.2";
+ private static final byte[] TEST_NEW_CC_IP_SHA256 = HexDump.hexStringToByteArray(
+ "721BAB5E313CF0CC76B10F9592F18B9D1B8996497501A3306A55B3AE9F1CC87C");
+ private static final byte[] TEST_NEW_CC_IP_CRC32 = HexDump.hexStringToByteArray("940B8BEE");
+
+ private Context mContext;
+ private File mTestXmlFile;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mTestXmlFile = new File(mContext.getFilesDir(), "test_watchlist_settings.xml");
+ mTestXmlFile.delete();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mTestXmlFile.delete();
+ }
+
+ @Test
+ public void testWatchlistSettings_parsing() throws Exception {
+ copyWatchlistSettingsXml(mContext, TEST_XML_1, mTestXmlFile);
+ WatchlistSettings settings = new WatchlistSettings(mTestXmlFile);
+ assertTrue(settings.containsDomain(TEST_CC_DOMAIN));
+ assertTrue(settings.containsIp(TEST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
+ assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
+ }
+
+ @Test
+ public void testWatchlistSettings_writeSettingsToDisk() throws Exception {
+ copyWatchlistSettingsXml(mContext, TEST_XML_1, mTestXmlFile);
+ WatchlistSettings settings = new WatchlistSettings(mTestXmlFile);
+ settings.writeSettingsToDisk(Arrays.asList(TEST_NEW_CC_DOMAIN_CRC32),
+ Arrays.asList(TEST_NEW_CC_DOMAIN_SHA256), Arrays.asList(TEST_NEW_CC_IP_CRC32),
+ Arrays.asList(TEST_NEW_CC_IP_SHA256));
+ // Ensure old watchlist is not in memory
+ assertFalse(settings.containsDomain(TEST_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
+ assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
+ // Ensure new watchlist is in memory
+ assertTrue(settings.containsDomain(TEST_NEW_CC_DOMAIN));
+ assertTrue(settings.containsIp(TEST_NEW_CC_IP));
+ // Reload settings from disk and test again
+ settings = new WatchlistSettings(mTestXmlFile);
+ // Ensure old watchlist is not in memory
+ assertFalse(settings.containsDomain(TEST_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
+ assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
+ // Ensure new watchlist is in memory
+ assertTrue(settings.containsDomain(TEST_NEW_CC_DOMAIN));
+ assertTrue(settings.containsIp(TEST_NEW_CC_IP));
+ }
+
+ @Test
+ public void testWatchlistSettings_writeSettingsToMemory() throws Exception {
+ copyWatchlistSettingsXml(mContext, TEST_XML_1, mTestXmlFile);
+ WatchlistSettings settings = new WatchlistSettings(mTestXmlFile);
+ settings.writeSettingsToMemory(Arrays.asList(TEST_NEW_CC_DOMAIN_CRC32),
+ Arrays.asList(TEST_NEW_CC_DOMAIN_SHA256), Arrays.asList(TEST_NEW_CC_IP_CRC32),
+ Arrays.asList(TEST_NEW_CC_IP_SHA256));
+ // Ensure old watchlist is not in memory
+ assertFalse(settings.containsDomain(TEST_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
+ assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
+ // Ensure new watchlist is in memory
+ assertTrue(settings.containsDomain(TEST_NEW_CC_DOMAIN));
+ assertTrue(settings.containsIp(TEST_NEW_CC_IP));
+ // Reload settings from disk and test again
+ settings = new WatchlistSettings(mTestXmlFile);
+ // Ensure old watchlist is in memory
+ assertTrue(settings.containsDomain(TEST_CC_DOMAIN));
+ assertTrue(settings.containsIp(TEST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
+ assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
+ // Ensure new watchlist is not in memory
+ assertFalse(settings.containsDomain(TEST_NEW_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_NEW_CC_IP));;
+ }
+
+ private static void copyWatchlistSettingsXml(Context context, String xmlAsset, File outFile)
+ throws IOException {
+ writeToFile(outFile, readAsset(context, xmlAsset));
+
+ }
+
+ private static String readAsset(Context context, String assetPath) throws IOException {
+ final StringBuilder sb = new StringBuilder();
+ try (BufferedReader br = new BufferedReader(
+ new InputStreamReader(
+ context.getResources().getAssets().open(assetPath)))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ sb.append(line);
+ sb.append(System.lineSeparator());
+ }
+ }
+ return sb.toString();
+ }
+
+ private static void writeToFile(File path, String content)
+ throws IOException {
+ path.getParentFile().mkdirs();
+
+ try (FileWriter writer = new FileWriter(path)) {
+ writer.write(content);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 67ffe58..39d256a 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -16,16 +16,14 @@
package com.android.server.usage;
-import static android.app.usage.AppStandby.REASON_TIMEOUT;
-import static android.app.usage.AppStandby.STANDBY_BUCKET_ACTIVE;
-import static android.app.usage.AppStandby.STANDBY_BUCKET_RARE;
-
import android.app.usage.AppStandby;
import android.os.FileUtils;
import android.test.AndroidTestCase;
import java.io.File;
+import static android.app.usage.AppStandby.*;
+
public class AppIdleHistoryTests extends AndroidTestCase {
File mStorageDir;
@@ -111,5 +109,9 @@
assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 5000), STANDBY_BUCKET_RARE);
assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 5000), STANDBY_BUCKET_ACTIVE);
assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_TIMEOUT);
+
+ assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
+ assertFalse(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
+ assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_FREQUENT));
}
}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 9846d6f..8531baf5 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -16,11 +16,7 @@
package com.android.server.usage;
-import static android.app.usage.AppStandby.STANDBY_BUCKET_ACTIVE;
-import static android.app.usage.AppStandby.STANDBY_BUCKET_FREQUENT;
-import static android.app.usage.AppStandby.STANDBY_BUCKET_RARE;
-import static android.app.usage.AppStandby.STANDBY_BUCKET_WORKING_SET;
-
+import static android.app.usage.AppStandby.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -251,10 +247,22 @@
false));
}
+ private void reportEvent(AppStandbyController controller, long elapsedTime) {
+ // Back to ACTIVE on event
+ UsageEvents.Event ev = new UsageEvents.Event();
+ ev.mPackage = PACKAGE_1;
+ ev.mEventType = UsageEvents.Event.USER_INTERACTION;
+ controller.reportEvent(ev, elapsedTime, USER_ID);
+ }
+
@Test
public void testBuckets() throws Exception {
AppStandbyController controller = setupController();
+ assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);
+
+ reportEvent(controller, 0);
+
// ACTIVE bucket
assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
@@ -270,11 +278,7 @@
// RARE bucket
assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_RARE);
- // Back to ACTIVE on event
- UsageEvents.Event ev = new UsageEvents.Event();
- ev.mPackage = PACKAGE_1;
- ev.mEventType = UsageEvents.Event.USER_INTERACTION;
- controller.reportEvent(ev, mInjector.mElapsedRealtime, USER_ID);
+ reportEvent(controller, 9 * DAY_MS);
assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_ACTIVE);
@@ -287,6 +291,10 @@
AppStandbyController controller = setupController();
mInjector.setDisplayOn(false);
+ assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);
+
+ reportEvent(controller, 0);
+
// ACTIVE bucket
assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index e5d3915..c5ca330 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -103,7 +103,7 @@
long lastUsedScreenTime;
@StandbyBuckets int currentBucket;
String bucketingReason;
- int lastInformedState;
+ int lastInformedBucket;
}
AppIdleHistory(File storageDir, long elapsedRealtime) {
@@ -333,13 +333,12 @@
}
boolean shouldInformListeners(String packageName, int userId,
- long elapsedRealtime, boolean isIdle) {
+ long elapsedRealtime, int bucket) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
elapsedRealtime, true);
- int targetState = isIdle? STATE_IDLE : STATE_ACTIVE;
- if (appUsageHistory.lastInformedState != (isIdle ? STATE_IDLE : STATE_ACTIVE)) {
- appUsageHistory.lastInformedState = targetState;
+ if (appUsageHistory.lastInformedBucket != bucket) {
+ appUsageHistory.lastInformedBucket = bucket;
return true;
}
return false;
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 17fde57..5623a68 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -160,7 +160,6 @@
private final Context mContext;
// TODO: Provide a mechanism to set an external bucketing service
- private boolean mUseInternalBucketingHeuristics = true;
private AppWidgetManager mAppWidgetManager;
private PowerManager mPowerManager;
@@ -367,29 +366,33 @@
Slog.d(TAG, " Checking idle state for " + packageName);
}
if (isSpecial) {
- maybeInformListeners(packageName, userId, elapsedRealtime, false);
- } else if (mUseInternalBucketingHeuristics) {
+ maybeInformListeners(packageName, userId, elapsedRealtime,
+ AppStandby.STANDBY_BUCKET_ACTIVE);
+ } else {
synchronized (mAppIdleLock) {
- int oldBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId,
- elapsedRealtime);
String bucketingReason = mAppIdleHistory.getAppStandbyReason(packageName,
userId, elapsedRealtime);
- if (bucketingReason != null
- && (bucketingReason.equals(AppStandby.REASON_FORCED)
- || bucketingReason.startsWith(AppStandby.REASON_PREDICTED))) {
+ // If the bucket was forced by the developer, leave it alone
+ if (AppStandby.REASON_FORCED.equals(bucketingReason)) {
continue;
}
- int newBucket = getBucketForLocked(packageName, userId,
- elapsedRealtime);
- if (DEBUG) {
- Slog.d(TAG, " Old bucket=" + oldBucket
- + ", newBucket=" + newBucket);
- }
- if (oldBucket != newBucket) {
- mAppIdleHistory.setAppStandbyBucket(packageName, userId,
- elapsedRealtime, newBucket, AppStandby.REASON_TIMEOUT);
- maybeInformListeners(packageName, userId, elapsedRealtime,
- newBucket >= AppStandby.STANDBY_BUCKET_RARE);
+ // If the bucket was moved up due to usage, let the timeouts apply.
+ if (AppStandby.REASON_USAGE.equals(bucketingReason)
+ || AppStandby.REASON_TIMEOUT.equals(bucketingReason)) {
+ int oldBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId,
+ elapsedRealtime);
+ int newBucket = getBucketForLocked(packageName, userId,
+ elapsedRealtime);
+ if (DEBUG) {
+ Slog.d(TAG, " Old bucket=" + oldBucket
+ + ", newBucket=" + newBucket);
+ }
+ if (oldBucket < newBucket) {
+ mAppIdleHistory.setAppStandbyBucket(packageName, userId,
+ elapsedRealtime, newBucket, AppStandby.REASON_TIMEOUT);
+ maybeInformListeners(packageName, userId, elapsedRealtime,
+ newBucket);
+ }
}
}
}
@@ -403,12 +406,12 @@
}
private void maybeInformListeners(String packageName, int userId,
- long elapsedRealtime, boolean isIdle) {
+ long elapsedRealtime, int bucket) {
synchronized (mAppIdleLock) {
if (mAppIdleHistory.shouldInformListeners(packageName, userId,
- elapsedRealtime, isIdle)) {
+ elapsedRealtime, bucket)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
- userId, isIdle ? 1 : 0, packageName));
+ userId, bucket, packageName));
}
}
}
@@ -461,11 +464,13 @@
if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
boolean paroled = false;
synchronized (mAppIdleLock) {
- final long timeSinceLastParole = mInjector.currentTimeMillis() - mLastAppIdleParoledTime;
+ final long timeSinceLastParole =
+ mInjector.currentTimeMillis() - mLastAppIdleParoledTime;
if (!deviceIdle
&& timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
if (DEBUG) {
- Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
+ Slog.i(TAG,
+ "Bringing idle apps out of inactive state due to deviceIdleMode=false");
}
paroled = true;
} else if (deviceIdle) {
@@ -491,7 +496,8 @@
|| event.mEventType == UsageEvents.Event.USER_INTERACTION)) {
mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
if (previouslyIdle) {
- maybeInformListeners(event.mPackage, userId, elapsedRealtime, false);
+ maybeInformListeners(event.mPackage, userId, elapsedRealtime,
+ AppStandby.STANDBY_BUCKET_ACTIVE);
notifyBatteryStats(event.mPackage, userId, false);
}
}
@@ -729,7 +735,8 @@
void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
String reason, long elapsedRealtime) {
- mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, reason);
+ mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
+ reason);
}
private boolean isActiveDeviceAdmin(String packageName, int userId) {
@@ -786,9 +793,10 @@
return packageName != null && packageName.equals(activeScorer);
}
- void informListeners(String packageName, int userId, boolean isIdle) {
+ void informListeners(String packageName, int userId, int bucket) {
+ final boolean idle = bucket >= AppStandby.STANDBY_BUCKET_RARE;
for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
- listener.onAppIdleStateChanged(packageName, userId, isIdle);
+ listener.onAppIdleStateChanged(packageName, userId, idle, bucket);
}
}
@@ -1037,7 +1045,7 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_INFORM_LISTENERS:
- informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
+ informListeners((String) msg.obj, msg.arg1, msg.arg2);
break;
case MSG_FORCE_IDLE_STATE:
@@ -1187,7 +1195,8 @@
mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue,
SCREEN_TIME_THRESHOLDS);
- String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS, null);
+ String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS,
+ null);
mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue,
ELAPSED_TIME_THRESHOLDS);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 061e55a..0030ab6 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1650,6 +1650,26 @@
public static final String KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL =
"identify_high_definition_calls_in_call_log_bool";
+ /**
+ * Flag specifying whether to use the {@link ServiceState} roaming status, which can be
+ * affected by other carrier configs (e.g.
+ * {@link #KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY}), when setting the SPN display.
+ * <p>
+ * If {@code true}, the SPN display uses {@link ServiceState#getRoaming}.
+ * If {@code false} the SPN display checks if the current MCC/MNC is different from the
+ * SIM card's MCC/MNC.
+ *
+ * @see KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
+ * @see KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
+ * @see KEY_NON_ROAMING_OPERATOR_STRING_ARRAY
+ * @see KEY_ROAMING_OPERATOR_STRING_ARRAY
+ * @see KEY_FORCE_HOME_NETWORK_BOOL
+ *
+ * @hide
+ */
+ public static final String KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL =
+ "spn_display_rule_use_roaming_from_service_state_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -1928,6 +1948,7 @@
sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL, false);
+ sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false);
}
/**
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index afff6d5..9ccfa94 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -204,16 +204,6 @@
public static final int LISTEN_VOLTE_STATE = 0x00004000;
/**
- * Listen for OEM hook raw event
- *
- * @see #onOemHookRawEvent
- * @hide
- * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
- */
- @Deprecated
- public static final int LISTEN_OEM_HOOK_RAW_EVENT = 0x00008000;
-
- /**
* Listen for carrier network changes indicated by a carrier app.
*
* @see #onCarrierNetworkRequest
@@ -359,9 +349,6 @@
case LISTEN_DATA_ACTIVATION_STATE:
PhoneStateListener.this.onDataActivationStateChanged((int)msg.obj);
break;
- case LISTEN_OEM_HOOK_RAW_EVENT:
- PhoneStateListener.this.onOemHookRawEvent((byte[])msg.obj);
- break;
case LISTEN_CARRIER_NETWORK_CHANGE:
PhoneStateListener.this.onCarrierNetworkChange((boolean)msg.obj);
break;
@@ -556,16 +543,6 @@
}
/**
- * Callback invoked when OEM hook raw event is received. Requires
- * the READ_PRIVILEGED_PHONE_STATE permission.
- * @param rawData is the byte array of the OEM hook raw data.
- * @hide
- */
- public void onOemHookRawEvent(byte[] rawData) {
- // default implementation empty
- }
-
- /**
* Callback invoked when telephony has received notice from a carrier
* app that a network action that could result in connectivity loss
* has been requested by an app using
@@ -677,10 +654,6 @@
send(LISTEN_DATA_ACTIVATION_STATE, 0, 0, activationState);
}
- public void onOemHookRawEvent(byte[] rawData) {
- send(LISTEN_OEM_HOOK_RAW_EVENT, 0, 0, rawData);
- }
-
public void onCarrierNetworkChange(boolean active) {
send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 42c3de5..4ffb3c3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5709,29 +5709,6 @@
return retVal;
}
- /**
- * Returns the result and response from RIL for oem request
- *
- * @param oemReq the data is sent to ril.
- * @param oemResp the respose data from RIL.
- * @return negative value request was not handled or get error
- * 0 request was handled succesfully, but no response data
- * positive value success, data length of response
- * @hide
- * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
- */
- @Deprecated
- public int invokeOemRilRequestRaw(byte[] oemReq, byte[] oemResp) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- return telephony.invokeOemRilRequestRaw(oemReq, oemResp);
- } catch (RemoteException ex) {
- } catch (NullPointerException ex) {
- }
- return -1;
- }
-
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index e9c5461..ac16139 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -45,7 +45,6 @@
void onVoLteServiceStateChanged(in VoLteServiceState lteState);
void onVoiceActivationStateChanged(int activationState);
void onDataActivationStateChanged(int activationState);
- void onOemHookRawEvent(in byte[] rawData);
void onCarrierNetworkChange(in boolean active);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 2ac11b5..3cc9bde 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1000,17 +1000,6 @@
in List<String> cdmaNonRoamingList);
/**
- * Returns the result and response from RIL for oem request
- *
- * @param oemReq the data is sent to ril.
- * @param oemResp the respose data from RIL.
- * @return negative value request was not handled or get error
- * 0 request was handled succesfully, but no response data
- * positive value success, data length of response
- */
- int invokeOemRilRequestRaw(in byte[] oemReq, out byte[] oemResp);
-
- /**
* Check if any mobile Radios need to be shutdown.
*
* @return true is any mobile radio needs to be shutdown
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 2c2206c..75d8f3f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -67,7 +67,6 @@
void notifyVoLteServiceStateChanged(in VoLteServiceState lteState);
void notifySimActivationStateChangedForPhoneId(in int phoneId, in int subId,
int activationState, int activationType);
- void notifyOemHookRawEventForSubscriber(in int subId, in byte[] rawData);
void notifySubscriptionInfoChanged();
void notifyCarrierNetworkChange(in boolean active);
}
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index 3fa9c3a..fcbb9da 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -19,12 +19,14 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
import android.net.MacAddress.MacAddressType;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import java.util.Arrays;
+import java.util.Random;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -64,10 +66,139 @@
String msg = String.format("expected type of %s to be %s, but got %s",
Arrays.toString(t.addr), t.expected, got);
assertEquals(msg, t.expected, got);
+
+ if (got != null) {
+ assertEquals(got, new MacAddress(t.addr).addressType());
+ }
}
}
- static byte[] toByteArray(int[] in) {
+ @Test
+ public void testIsMulticastAddress() {
+ MacAddress[] multicastAddresses = {
+ MacAddress.BROADCAST_ADDRESS,
+ new MacAddress("07:00:d3:56:8a:c4"),
+ new MacAddress("33:33:aa:bb:cc:dd"),
+ };
+ MacAddress[] unicastAddresses = {
+ MacAddress.ALL_ZEROS_ADDRESS,
+ new MacAddress("00:01:44:55:66:77"),
+ new MacAddress("08:00:22:33:44:55"),
+ new MacAddress("06:00:00:00:00:00"),
+ };
+
+ for (MacAddress mac : multicastAddresses) {
+ String msg = mac.toString() + " expected to be a multicast address";
+ assertTrue(msg, mac.isMulticastAddress());
+ }
+ for (MacAddress mac : unicastAddresses) {
+ String msg = mac.toString() + " expected not to be a multicast address";
+ assertFalse(msg, mac.isMulticastAddress());
+ }
+ }
+
+ @Test
+ public void testIsLocallyAssignedAddress() {
+ MacAddress[] localAddresses = {
+ new MacAddress("06:00:00:00:00:00"),
+ new MacAddress("07:00:d3:56:8a:c4"),
+ new MacAddress("33:33:aa:bb:cc:dd"),
+ };
+ MacAddress[] universalAddresses = {
+ new MacAddress("00:01:44:55:66:77"),
+ new MacAddress("08:00:22:33:44:55"),
+ };
+
+ for (MacAddress mac : localAddresses) {
+ String msg = mac.toString() + " expected to be a locally assigned address";
+ assertTrue(msg, mac.isLocallyAssigned());
+ }
+ for (MacAddress mac : universalAddresses) {
+ String msg = mac.toString() + " expected not to be globally unique address";
+ assertFalse(msg, mac.isLocallyAssigned());
+ }
+ }
+
+ @Test
+ public void testMacAddressConversions() {
+ final int iterations = 10000;
+ for (int i = 0; i < iterations; i++) {
+ MacAddress mac = MacAddress.getRandomAddress();
+
+ String stringRepr = mac.toString();
+ byte[] bytesRepr = mac.toByteArray();
+
+ assertEquals(mac, new MacAddress(stringRepr));
+ assertEquals(mac, new MacAddress(bytesRepr));
+ }
+ }
+
+ @Test
+ public void testMacAddressRandomGeneration() {
+ final int iterations = 1000;
+ final String expectedAndroidOui = "da:a1:19";
+ for (int i = 0; i < iterations; i++) {
+ MacAddress mac = MacAddress.getRandomAddress();
+ String stringRepr = mac.toString();
+
+ assertTrue(stringRepr + " expected to be a locally assigned address",
+ mac.isLocallyAssigned());
+ assertTrue(stringRepr + " expected to begin with " + expectedAndroidOui,
+ stringRepr.startsWith(expectedAndroidOui));
+ }
+
+ final Random r = new Random();
+ final String anotherOui = "24:5f:78";
+ final String expectedLocalOui = "26:5f:78";
+ final MacAddress base = new MacAddress(anotherOui + ":0:0:0");
+ for (int i = 0; i < iterations; i++) {
+ MacAddress mac = MacAddress.getRandomAddress(base, r);
+ String stringRepr = mac.toString();
+
+ assertTrue(stringRepr + " expected to be a locally assigned address",
+ mac.isLocallyAssigned());
+ assertTrue(stringRepr + " expected to begin with " + expectedLocalOui,
+ stringRepr.startsWith(expectedLocalOui));
+ }
+ }
+
+ @Test
+ public void testConstructorInputValidation() {
+ String[] invalidStringAddresses = {
+ null,
+ "",
+ "abcd",
+ "1:2:3:4:5",
+ "1:2:3:4:5:6:7",
+ "10000:2:3:4:5:6",
+ };
+
+ for (String s : invalidStringAddresses) {
+ try {
+ MacAddress mac = new MacAddress(s);
+ fail("new MacAddress(" + s + ") should have failed, but returned " + mac);
+ } catch (IllegalArgumentException excepted) {
+ }
+ }
+
+ byte[][] invalidBytesAddresses = {
+ null,
+ {},
+ {1,2,3,4,5},
+ {1,2,3,4,5,6,7},
+ };
+
+ for (byte[] b : invalidBytesAddresses) {
+ try {
+ MacAddress mac = new MacAddress(b);
+ fail("new MacAddress(" + Arrays.toString(b)
+ + ") should have failed, but returned " + mac);
+ } catch (IllegalArgumentException excepted) {
+ }
+ }
+ }
+
+ static byte[] toByteArray(int... in) {
byte[] out = new byte[in.length];
for (int i = 0; i < in.length; i++) {
out[i] = (byte) in[i];