Merge changes Iae56bcd2,I5b2e37c3
* changes:
Fixed logic issue with WC.supportsSplitScreenWindowingMode()
Don't move home task forward in some cases.
diff --git a/api/current.txt b/api/current.txt
index 6a8bb01..97b065e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6541,6 +6541,7 @@
field public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = 2; // 0x2
field public static final int LOCK_TASK_FEATURE_RECENTS = 8; // 0x8
field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
+ field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
@@ -31987,6 +31988,7 @@
field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
+ field public static final java.lang.String DISALLOW_USER_SWITCH = "no_user_switch";
field public static final java.lang.String ENSURE_VERIFY_APPS = "ensure_verify_apps";
field public static final java.lang.String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1
@@ -38429,6 +38431,16 @@
field public final int errno;
}
+ public class Int32Ref {
+ ctor public Int32Ref(int);
+ field public int value;
+ }
+
+ public class Int64Ref {
+ ctor public Int64Ref(long);
+ field public long value;
+ }
+
public final class Os {
method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
method public static boolean access(java.lang.String, int) throws android.system.ErrnoException;
@@ -38498,7 +38510,8 @@
method public static void remove(java.lang.String) throws android.system.ErrnoException;
method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
- method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static void setegid(int) throws android.system.ErrnoException;
@@ -38523,7 +38536,8 @@
method public static int umask(int);
method public static android.system.StructUtsname uname();
method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
- method public static int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int writev(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
diff --git a/api/system-current.txt b/api/system-current.txt
index e485e964e..61aaf18 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6788,6 +6788,7 @@
field public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = 2; // 0x2
field public static final int LOCK_TASK_FEATURE_RECENTS = 8; // 0x8
field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
+ field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
@@ -34840,6 +34841,7 @@
field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
+ field public static final java.lang.String DISALLOW_USER_SWITCH = "no_user_switch";
field public static final java.lang.String ENSURE_VERIFY_APPS = "ensure_verify_apps";
field public static final java.lang.String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
field public static final int RESTRICTION_NOT_SET = 0; // 0x0
@@ -41730,6 +41732,16 @@
field public final int errno;
}
+ public class Int32Ref {
+ ctor public Int32Ref(int);
+ field public int value;
+ }
+
+ public class Int64Ref {
+ ctor public Int64Ref(long);
+ field public long value;
+ }
+
public final class Os {
method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
method public static boolean access(java.lang.String, int) throws android.system.ErrnoException;
@@ -41799,7 +41811,8 @@
method public static void remove(java.lang.String) throws android.system.ErrnoException;
method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
- method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static void setegid(int) throws android.system.ErrnoException;
@@ -41824,7 +41837,8 @@
method public static int umask(int);
method public static android.system.StructUtsname uname();
method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
- method public static int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int writev(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
diff --git a/api/test-current.txt b/api/test-current.txt
index 40f732c..2a2967e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6615,6 +6615,7 @@
field public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = 2; // 0x2
field public static final int LOCK_TASK_FEATURE_RECENTS = 8; // 0x8
field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
+ field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
@@ -12308,6 +12309,7 @@
method public static int getWALAutoCheckpoint();
method public static int getWALConnectionPoolSize();
method public static java.lang.String getWALSyncMode();
+ method public static boolean isCompatibilityWalSupported();
method public static int releaseMemory();
}
@@ -32255,6 +32257,7 @@
field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
+ field public static final java.lang.String DISALLOW_USER_SWITCH = "no_user_switch";
field public static final java.lang.String ENSURE_VERIFY_APPS = "ensure_verify_apps";
field public static final java.lang.String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1
@@ -35545,6 +35548,7 @@
field public static final java.lang.String ALLOWED_GEOLOCATION_ORIGINS = "allowed_geolocation_origins";
field public static final deprecated java.lang.String ALLOW_MOCK_LOCATION = "mock_location";
field public static final java.lang.String ANDROID_ID = "android_id";
+ field public static final java.lang.String AUTOFILL_FEATURE_FIELD_DETECTION = "autofill_field_detection";
field public static final java.lang.String AUTOFILL_SERVICE = "autofill_service";
field public static final deprecated java.lang.String BACKGROUND_DATA = "background_data";
field public static final deprecated java.lang.String BLUETOOTH_ON = "bluetooth_on";
@@ -37600,6 +37604,13 @@
method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.widget.RemoteViews);
}
+ public final class FieldsDetection implements android.os.Parcelable {
+ ctor public FieldsDetection(android.view.autofill.AutofillId, java.lang.String, java.lang.String);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FieldsDetection> CREATOR;
+ }
+
public final class FillCallback {
method public void onFailure(java.lang.CharSequence);
method public void onSuccess(android.service.autofill.FillResponse);
@@ -37625,6 +37636,7 @@
method public java.util.Map<android.view.autofill.AutofillId, java.lang.String> getChangedFields();
method public android.os.Bundle getClientState();
method public java.lang.String getDatasetId();
+ method public java.util.Map<java.lang.String, java.lang.Integer> getDetectedFields();
method public java.util.Set<java.lang.String> getIgnoredDatasetIds();
method public java.util.Map<android.view.autofill.AutofillId, java.util.Set<java.lang.String>> getManuallyEnteredField();
method public java.util.Set<java.lang.String> getSelectedDatasetIds();
@@ -37662,6 +37674,7 @@
method public android.service.autofill.FillResponse.Builder disableAutofill(long);
method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews);
method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle);
+ method public android.service.autofill.FillResponse.Builder setFieldsDetection(android.service.autofill.FieldsDetection);
method public android.service.autofill.FillResponse.Builder setFlags(int);
method public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...);
method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
@@ -38809,6 +38822,16 @@
field public final int errno;
}
+ public class Int32Ref {
+ ctor public Int32Ref(int);
+ field public int value;
+ }
+
+ public class Int64Ref {
+ ctor public Int64Ref(long);
+ field public long value;
+ }
+
public final class Os {
method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
method public static boolean access(java.lang.String, int) throws android.system.ErrnoException;
@@ -38878,7 +38901,8 @@
method public static void remove(java.lang.String) throws android.system.ErrnoException;
method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
- method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static void setegid(int) throws android.system.ErrnoException;
@@ -38903,7 +38927,8 @@
method public static int umask(int);
method public static android.system.StructUtsname uname();
method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
- method public static int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int writev(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
diff --git a/config/compiled-classes-phone b/config/compiled-classes-phone
index 7703fdc..bc344d7 100644
--- a/config/compiled-classes-phone
+++ b/config/compiled-classes-phone
@@ -3943,9 +3943,6 @@
android.telephony.VoLteServiceState
android.telephony.VoLteServiceState$1
android.telephony.gsm.GsmCellLocation
-android.telephony.ims.ImsServiceProxy$INotifyStatusChanged
-android.telephony.ims.ImsServiceProxyCompat
-android.telephony.ims.feature.IMMTelFeature
android.telephony.ims.stub.ImsConfigImplBase
android.telephony.ims.stub.ImsEcbmImplBase
android.telephony.ims.stub.ImsUtImplBase
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a2e5cc9..f0226b7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6097,8 +6097,8 @@
/**
* Flag used by {@link #createAndManageUser} to specify that the user should be created
- * ephemeral.
- * @hide
+ * ephemeral. Ephemeral users will be removed after switching to another user or rebooting the
+ * device.
*/
public static final int MAKE_USER_EPHEMERAL = 0x0002;
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/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 361b81b..b692039 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -416,7 +416,8 @@
boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled
!= mConfiguration.foreignKeyConstraintsEnabled;
boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
- & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
+ & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0
+ || configuration.useCompatibilityWal != mConfiguration.useCompatibilityWal;
boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);
// Update configuration parameters.
diff --git a/core/java/android/database/sqlite/SQLiteGlobal.java b/core/java/android/database/sqlite/SQLiteGlobal.java
index bb2a517..d6d9764 100644
--- a/core/java/android/database/sqlite/SQLiteGlobal.java
+++ b/core/java/android/database/sqlite/SQLiteGlobal.java
@@ -83,7 +83,6 @@
/**
* Returns true if compatibility WAL mode is supported. In this mode, only
* database journal mode is changed. Connection pool will use at most one connection.
- * @hide
*/
public static boolean isCompatibilityWalSupported() {
return SystemProperties.getBoolean("debug.sqlite.compatibility_wal_supported",
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 16b1452..64f8f39 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -78,7 +78,11 @@
/**
* AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm.
*
- * <p>Valid lengths for this key are {128, 192, 256}.
+ * <p>Valid lengths for keying material are {160, 224, 288}.
+ *
+ * <p>As per RFC4106 (Section 8.1), keying material consists of a 128, 192, or 256 bit AES key
+ * followed by a 32-bit salt. RFC compliance requires that the salt must be unique per
+ * invocation with the same key.
*
* <p>Valid ICV (truncation) lengths are {64, 96, 128}.
*/
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index d7b3256..eccd5f4 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -136,7 +136,7 @@
}
@Override
- protected void finalize() {
+ protected void finalize() throws Throwable {
if (mCloseGuard != null) {
mCloseGuard.warnIfOpen();
}
diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java
index 05c8afb..6e4a231 100644
--- a/core/java/android/net/LocalSocketImpl.java
+++ b/core/java/android/net/LocalSocketImpl.java
@@ -16,18 +16,18 @@
package android.net;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.InputStream;
-import java.io.FileDescriptor;
-import java.net.SocketOptions;
-
import android.system.ErrnoException;
+import android.system.Int32Ref;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructLinger;
import android.system.StructTimeval;
-import android.util.MutableInt;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.SocketOptions;
/**
* Socket implementation used for android.net.LocalSocket and
@@ -62,7 +62,7 @@
FileDescriptor myFd = fd;
if (myFd == null) throw new IOException("socket closed");
- MutableInt avail = new MutableInt(0);
+ Int32Ref avail = new Int32Ref(0);
try {
Os.ioctlInt(myFd, OsConstants.FIONREAD, avail);
} catch (ErrnoException e) {
@@ -167,7 +167,7 @@
if (myFd == null) throw new IOException("socket closed");
// Loop until the output buffer is empty.
- MutableInt pending = new MutableInt(0);
+ Int32Ref pending = new Int32Ref(0);
while (true) {
try {
// See linux/net/unix/af_unix.c
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c54b72d..28836e4 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -792,6 +792,19 @@
public static final String DISALLOW_AUTOFILL = "no_autofill";
/**
+ * Specifies if user switching is blocked on the current user.
+ *
+ * <p> This restriction can only be set by the device owner, it will be applied to all users.
+ *
+ * <p>The default value is <code>false</code>.
+ *
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_USER_SWITCH = "no_user_switch";
+
+ /**
* Application restriction key that is used to indicate the pending arrival
* of real restrictions for the app.
*
@@ -917,7 +930,7 @@
/**
* Returns whether switching users is currently allowed.
* <p>For instance switching users is not allowed if the current user is in a phone call,
- * or system user hasn't been unlocked yet
+ * system user hasn't been unlocked yet, or {@link #DISALLOW_USER_SWITCH} is set.
* @hide
*/
public boolean canSwitchUsers() {
@@ -927,7 +940,9 @@
boolean isSystemUserUnlocked = isUserUnlocked(UserHandle.SYSTEM);
boolean inCall = TelephonyManager.getDefault().getCallState()
!= TelephonyManager.CALL_STATE_IDLE;
- return (allowUserSwitchingWhenSystemUserLocked || isSystemUserUnlocked) && !inCall;
+ boolean isUserSwitchDisallowed = hasUserRestriction(DISALLOW_USER_SWITCH);
+ return (allowUserSwitchingWhenSystemUserLocked || isSystemUserUnlocked) && !inCall
+ && !isUserSwitchDisallowed;
}
/**
@@ -2298,6 +2313,9 @@
if (!supportsMultipleUsers()) {
return false;
}
+ if (hasUserRestriction(DISALLOW_USER_SWITCH)) {
+ return false;
+ }
// If Demo Mode is on, don't show user switcher
if (isDeviceInDemoMode(mContext)) {
return false;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0a20c43..1716305 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5317,6 +5317,15 @@
public static final String AUTOFILL_SERVICE = "autofill_service";
/**
+ * Experimental autofill feature.
+ *
+ * <p>TODO(b/67867469): remove once feature is finished
+ * @hide
+ */
+ @TestApi
+ public static final String AUTOFILL_FEATURE_FIELD_DETECTION = "autofill_field_detection";
+
+ /**
* @deprecated Use {@link android.provider.Settings.Global#DEVICE_PROVISIONED} instead
*/
@Deprecated
diff --git a/core/java/android/service/autofill/FieldsDetection.java b/core/java/android/service/autofill/FieldsDetection.java
new file mode 100644
index 0000000..550ecf6
--- /dev/null
+++ b/core/java/android/service/autofill/FieldsDetection.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 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.service.autofill;
+
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.autofill.AutofillId;
+
+/**
+ * Class by service to improve autofillable fields detection by tracking the meaning of fields
+ * manually edited by the user (when they match values provided by the service).
+ *
+ * TODO(b/67867469):
+ * - proper javadoc
+ * - unhide / remove testApi
+ * - add FieldsDetection management so service can set it just once and reference it in further
+ * calls to improve performance (and also API to refresh it)
+ * - rename to FieldsDetectionInfo or FieldClassification? (same for CTS tests)
+ * - add FieldsDetectionUnitTest once API is well-defined
+ * @hide
+ */
+@TestApi
+public final class FieldsDetection implements Parcelable {
+
+ private final AutofillId mFieldId;
+ private final String mRemoteId;
+ private final String mValue;
+
+ /**
+ * Creates a field detection for just one field / value pair.
+ *
+ * @param fieldId autofill id of the field in the screen.
+ * @param remoteId id used by the service to identify the field later.
+ * @param value field value known to the service.
+ *
+ * TODO(b/67867469):
+ * - proper javadoc
+ * - change signature to allow more fields / values / match methods
+ * - might also need to use a builder, where the constructor is the id for the fieldsdetector
+ * - might need id for values as well
+ * - add @NonNull / check it / add unit tests
+ * - make 'value' input more generic so it can accept distance-based match and other matches
+ * - throw exception if field value is less than X characters (somewhere between 7-10)
+ * - make sure to limit total number of fields to around 10 or so
+ * - use AutofillValue instead of String (so it can compare dates, for example)
+ */
+ public FieldsDetection(AutofillId fieldId, String remoteId, String value) {
+ mFieldId = fieldId;
+ mRemoteId = remoteId;
+ mValue = value;
+ }
+
+ /** @hide */
+ public AutofillId getFieldId() {
+ return mFieldId;
+ }
+
+ /** @hide */
+ public String getRemoteId() {
+ return mRemoteId;
+ }
+
+ /** @hide */
+ public String getValue() {
+ return mValue;
+ }
+
+ /////////////////////////////////////
+ // Object "contract" methods. //
+ /////////////////////////////////////
+ @Override
+ public String toString() {
+ // Cannot disclose remoteId or value because they could contain PII
+ return new StringBuilder("FieldsDetection: [field=").append(mFieldId)
+ .append(", remoteId_length=").append(mRemoteId.length())
+ .append(", value_length=").append(mValue.length())
+ .append("]").toString();
+ }
+
+ /////////////////////////////////////
+ // Parcelable "contract" methods. //
+ /////////////////////////////////////
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeParcelable(mFieldId, flags);
+ parcel.writeString(mRemoteId);
+ parcel.writeString(mValue);
+ }
+
+ public static final Parcelable.Creator<FieldsDetection> CREATOR =
+ new Parcelable.Creator<FieldsDetection>() {
+ @Override
+ public FieldsDetection createFromParcel(Parcel parcel) {
+ // TODO(b/67867469): remove comment below if it does not use a builder at the end
+ // Always go through the builder to ensure the data ingested by
+ // the system obeys the contract of the builder to avoid attacks
+ // using specially crafted parcels.
+ return new FieldsDetection(parcel.readParcelable(null), parcel.readString(),
+ parcel.readString());
+ }
+
+ @Override
+ public FieldsDetection[] newArray(int size) {
+ return new FieldsDetection[size];
+ }
+ };
+}
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index b1857b3..736d9ef 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.IntentSender;
import android.os.Bundle;
import android.os.Parcel;
@@ -164,6 +165,10 @@
dest.writeStringList(event.mManuallyFilledDatasetIds.get(j));
}
}
+ dest.writeString(event.mDetectedRemoteId);
+ if (event.mDetectedRemoteId != null) {
+ dest.writeInt(event.mDetectedFieldScore);
+ }
}
}
}
@@ -226,6 +231,7 @@
* <p>See {@link android.view.autofill.AutofillManager} for more information about autofill
* contexts.
*/
+ // TODO(b/67867469): update with field detection behavior
public static final int TYPE_CONTEXT_COMMITTED = 4;
/** @hide */
@@ -253,6 +259,9 @@
@Nullable private final ArrayList<AutofillId> mManuallyFilledFieldIds;
@Nullable private final ArrayList<ArrayList<String>> mManuallyFilledDatasetIds;
+ @Nullable private final String mDetectedRemoteId;
+ private final int mDetectedFieldScore;
+
/**
* Returns the type of the event.
*
@@ -355,6 +364,39 @@
}
/**
+ * Gets the results of the last {@link FieldsDetection} request.
+ *
+ * @return map of edit-distance match ({@code 0} means full match,
+ * {@code 1} means 1 character different, etc...) by remote id (as set in the
+ * {@link FieldsDetection} constructor), or {@code null} if none of the user-input values
+ * matched the requested detection.
+ *
+ * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}, when the
+ * service requested {@link FillResponse.Builder#setFieldsDetection(FieldsDetection) fields
+ * detection}.
+ *
+ * TODO(b/67867469):
+ * - improve javadoc
+ * - refine score meaning (for example, should 1 be different of -1?)
+ * - mention when it's set
+ * - unhide
+ * - unhide / remove testApi
+ * - add @NonNull / check it / add unit tests
+ *
+ * @hide
+ */
+ @TestApi
+ @NonNull public Map<String, Integer> getDetectedFields() {
+ if (mDetectedRemoteId == null || mDetectedFieldScore == -1) {
+ return Collections.emptyMap();
+ }
+
+ final ArrayMap<String, Integer> map = new ArrayMap<>(1);
+ map.put(mDetectedRemoteId, mDetectedFieldScore);
+ return map;
+ }
+
+ /**
* Returns which fields were available on datasets provided by the service but manually
* entered by the user.
*
@@ -430,7 +472,6 @@
* and belonged to datasets.
* @param manuallyFilledDatasetIds The ids of datasets that had values matching the
* respective entry on {@code manuallyFilledFieldIds}.
- *
* @throws IllegalArgumentException If the length of {@code changedFieldIds} and
* {@code changedDatasetIds} doesn't match.
* @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and
@@ -438,13 +479,15 @@
*
* @hide
*/
+ // TODO(b/67867469): document detection field parameters once stable
public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState,
@Nullable List<String> selectedDatasetIds,
@Nullable ArraySet<String> ignoredDatasetIds,
@Nullable ArrayList<AutofillId> changedFieldIds,
@Nullable ArrayList<String> changedDatasetIds,
@Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
- @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds) {
+ @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
+ @Nullable String detectedRemoteId, int detectedFieldScore) {
mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_CONTEXT_COMMITTED,
"eventType");
mDatasetId = datasetId;
@@ -467,6 +510,8 @@
}
mManuallyFilledFieldIds = manuallyFilledFieldIds;
mManuallyFilledDatasetIds = manuallyFilledDatasetIds;
+ mDetectedRemoteId = detectedRemoteId;
+ mDetectedFieldScore = detectedFieldScore;
}
@Override
@@ -479,6 +524,8 @@
+ ", changedDatasetsIds=" + mChangedDatasetIds
+ ", manuallyFilledFieldIds=" + mManuallyFilledFieldIds
+ ", manuallyFilledDatasetIds=" + mManuallyFilledDatasetIds
+ + ", detectedRemoteId=" + mDetectedRemoteId
+ + ", detectedFieldScore=" + mDetectedFieldScore
+ "]";
}
}
@@ -514,11 +561,15 @@
} else {
manuallyFilledDatasetIds = null;
}
+ final String detectedRemoteId = parcel.readString();
+ final int detectedFieldScore = detectedRemoteId == null ? -1
+ : parcel.readInt();
selection.addEvent(new Event(eventType, datasetId, clientState,
selectedDatasetIds, ignoredDatasets,
changedFieldIds, changedDatasetIds,
- manuallyFilledFieldIds, manuallyFilledDatasetIds));
+ manuallyFilledFieldIds, manuallyFilledDatasetIds,
+ detectedRemoteId, detectedFieldScore));
}
return selection;
}
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 2f6342a..4e6a884 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -22,6 +22,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.app.Activity;
import android.content.IntentSender;
import android.content.pm.ParceledListSlice;
@@ -75,6 +76,7 @@
private final @Nullable AutofillId[] mAuthenticationIds;
private final @Nullable AutofillId[] mIgnoredIds;
private final long mDisableDuration;
+ private final @Nullable FieldsDetection mFieldsDetection;
private final int mFlags;
private int mRequestId;
@@ -87,6 +89,7 @@
mAuthenticationIds = builder.mAuthenticationIds;
mIgnoredIds = builder.mIgnoredIds;
mDisableDuration = builder.mDisableDuration;
+ mFieldsDetection = builder.mFieldsDetection;
mFlags = builder.mFlags;
mRequestId = INVALID_REQUEST_ID;
}
@@ -132,6 +135,11 @@
}
/** @hide */
+ public @Nullable FieldsDetection getFieldsDetection() {
+ return mFieldsDetection;
+ }
+
+ /** @hide */
public int getFlags() {
return mFlags;
}
@@ -167,6 +175,7 @@
private AutofillId[] mAuthenticationIds;
private AutofillId[] mIgnoredIds;
private long mDisableDuration;
+ private FieldsDetection mFieldsDetection;
private int mFlags;
private boolean mDestroyed;
@@ -315,6 +324,25 @@
}
/**
+ * TODO(b/67867469):
+ * - javadoc it
+ * - javadoc how to check results
+ * - unhide
+ * - unhide / remove testApi
+ * - throw exception (and document) if response has datasets or saveinfo
+ * - throw exception (and document) if id on fieldsDetection is ignored
+ *
+ * @hide
+ */
+ @TestApi
+ public Builder setFieldsDetection(@NonNull FieldsDetection fieldsDetection) {
+ throwIfDestroyed();
+ throwIfDisableAutofillCalled();
+ mFieldsDetection = Preconditions.checkNotNull(fieldsDetection);
+ return this;
+ }
+
+ /**
* Sets flags changing the response behavior.
*
* @param flags a combination of {@link #FLAG_TRACK_CONTEXT_COMMITED} and
@@ -365,7 +393,8 @@
if (duration <= 0) {
throw new IllegalArgumentException("duration must be greater than 0");
}
- if (mAuthentication != null || mDatasets != null || mSaveInfo != null) {
+ if (mAuthentication != null || mDatasets != null || mSaveInfo != null
+ || mFieldsDetection != null) {
throw new IllegalStateException("disableAutofill() must be the only method called");
}
@@ -388,11 +417,11 @@
*/
public FillResponse build() {
throwIfDestroyed();
-
if (mAuthentication == null && mDatasets == null && mSaveInfo == null
- && mDisableDuration == 0) {
- throw new IllegalStateException("need to provide at least one DataSet or a "
- + "SaveInfo or an authentication with a presentation or disable autofill");
+ && mDisableDuration == 0 && mFieldsDetection == null) {
+ throw new IllegalStateException("need to provide: at least one DataSet, or a "
+ + "SaveInfo, or an authentication with a presentation, "
+ + "or a FieldsDetection, or disable autofill");
}
mDestroyed = true;
return new FillResponse(this);
@@ -430,6 +459,7 @@
.append(", ignoredIds=").append(Arrays.toString(mIgnoredIds))
.append(", disableDuration=").append(mDisableDuration)
.append(", flags=").append(mFlags)
+ .append(", fieldDetection=").append(mFieldsDetection)
.append("]")
.toString();
}
@@ -453,6 +483,7 @@
parcel.writeParcelable(mPresentation, flags);
parcel.writeParcelableArray(mIgnoredIds, flags);
parcel.writeLong(mDisableDuration);
+ parcel.writeParcelable(mFieldsDetection, flags);
parcel.writeInt(mFlags);
parcel.writeInt(mRequestId);
}
@@ -488,6 +519,10 @@
if (disableDuration > 0) {
builder.disableAutofill(disableDuration);
}
+ final FieldsDetection fieldsDetection = parcel.readParcelable(null);
+ if (fieldsDetection != null) {
+ builder.setFieldsDetection(fieldsDetection);
+ }
builder.setFlags(parcel.readInt());
final FillResponse response = builder.build();
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index 0b9552e..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]));
@@ -252,6 +255,7 @@
byte[] rootHash = new byte[DIGEST_SIZE_BYTES];
BufferedDigester digester = new BufferedDigester(salt, ByteBuffer.wrap(rootHash));
digester.consume(slice(output, 0, CHUNK_SIZE_BYTES));
+ digester.finish();
return rootHash;
}
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/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 2166f6e..7c76bab 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -70,6 +70,7 @@
* Name of the file that holds the shaders cache.
*/
private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache";
+ private static final String CACHE_PATH_SKIASHADERS = "com.android.skia.shaders_cache";
/**
* System property used to enable or disable threaded rendering profiling.
@@ -272,7 +273,9 @@
* @hide
*/
public static void setupDiskCache(File cacheDir) {
- ThreadedRenderer.setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath());
+ ThreadedRenderer.setupShadersDiskCache(
+ new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath(),
+ new File(cacheDir, CACHE_PATH_SKIASHADERS).getAbsolutePath());
}
/**
@@ -1007,7 +1010,7 @@
/** Not actually public - internal use only. This doc to make lint happy */
public static native void disableVsync();
- static native void setupShadersDiskCache(String cacheFile);
+ static native void setupShadersDiskCache(String cacheFile, String skiaCacheFile);
private static native void nRotateProcessStatsBuffer();
private static native void nSetProcessStatsBuffer(int fd);
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/android/widget/Editor.java b/core/java/android/widget/Editor.java
index e6da69d..7451d31 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -6557,12 +6557,12 @@
* Adds "PROCESS_TEXT" menu items to the specified menu.
*/
public void onInitializeMenu(Menu menu) {
- final int size = mSupportedActivities.size();
loadSupportedActivities();
+ final int size = mSupportedActivities.size();
for (int i = 0; i < size; i++) {
final ResolveInfo resolveInfo = mSupportedActivities.get(i);
menu.add(Menu.NONE, Menu.NONE,
- Editor.MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START + i++,
+ Editor.MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START + i,
getLabel(resolveInfo))
.setIntent(createProcessTextIntentForResolveInfo(resolveInfo))
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 8dc8cab..e91db13 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1354,6 +1354,7 @@
}
mDecorView = createDecorView(mBackgroundView);
+ mDecorView.setIsRootNamespace(true);
// The background owner should be elevated so that it casts a shadow.
mBackgroundView.setElevation(mElevation);
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 57263b1..870a0c2 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -54,6 +54,7 @@
#include <renderthread/RenderProxy.h>
#include <renderthread/RenderTask.h>
#include <renderthread/RenderThread.h>
+#include <pipeline/skia/ShaderCache.h>
namespace android {
@@ -970,10 +971,14 @@
// ----------------------------------------------------------------------------
static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz,
- jstring diskCachePath) {
+ jstring diskCachePath, jstring skiaDiskCachePath) {
const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL);
android::egl_set_cache_filename(cacheArray);
env->ReleaseStringUTFChars(diskCachePath, cacheArray);
+
+ const char* skiaCacheArray = env->GetStringUTFChars(skiaDiskCachePath, NULL);
+ uirenderer::skiapipeline::ShaderCache::get().setFilename(skiaCacheArray);
+ env->ReleaseStringUTFChars(skiaDiskCachePath, skiaCacheArray);
}
// ----------------------------------------------------------------------------
@@ -1018,7 +1023,7 @@
{ "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
{ "nSerializeDisplayListTree", "(J)V", (void*) android_view_ThreadedRenderer_serializeDisplayListTree },
{ "nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
- { "setupShadersDiskCache", "(Ljava/lang/String;)V",
+ { "setupShadersDiskCache", "(Ljava/lang/String;Ljava/lang/String;)V",
(void*) android_view_ThreadedRenderer_setupShadersDiskCache },
{ "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode},
{ "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode},
diff --git a/core/jni/hwbinder/EphemeralStorage.cpp b/core/jni/hwbinder/EphemeralStorage.cpp
index 4996bc8..3b18f2b 100644
--- a/core/jni/hwbinder/EphemeralStorage.cpp
+++ b/core/jni/hwbinder/EphemeralStorage.cpp
@@ -111,6 +111,7 @@
break; \
}
+__attribute__((no_sanitize("unsigned-integer-overflow")))
void EphemeralStorage::release(JNIEnv *env) {
for (size_t i = mItems.size(); i--;) {
const Item &item = mItems[i];
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
index b6986d5..0355f82 100644
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -48,6 +48,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Predicate;
/**
* Espresso utility methods for the floating toolbar.
@@ -175,31 +176,10 @@
* @throws AssertionError if the assertion fails
*/
public static void assertFloatingToolbarDoesNotContainItem(String itemLabel) {
- onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() {
- @Override
- public boolean matchesSafely(View view) {
- return doesNotContainItem(view);
- }
-
- @Override
- public void describeTo(Description description) {}
-
- private boolean doesNotContainItem(View view) {
- if (view.getTag() instanceof MenuItem) {
- if (itemLabel.equals(((MenuItem) view.getTag()).getTitle().toString())) {
- return false;
- }
- } else if (view instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup) view;
- for (int i = 0; i < viewGroup.getChildCount(); i++) {
- if (doesNotContainItem(viewGroup.getChildAt(i))) {
- return false;
- }
- }
- }
- return true;
- }
- }));
+ final Predicate<View> hasMenuItemLabel = view ->
+ view.getTag() instanceof MenuItem
+ && itemLabel.equals(((MenuItem) view.getTag()).getTitle().toString());
+ assertFloatingToolbarMenuItem(hasMenuItemLabel, false);
}
/**
@@ -209,23 +189,30 @@
* @throws AssertionError if the assertion fails
*/
public static void assertFloatingToolbarDoesNotContainItem(final int menuItemId) {
+ final Predicate<View> hasMenuItemId = view ->
+ view.getTag() instanceof MenuItem
+ && ((MenuItem) view.getTag()).getItemId() == menuItemId;
+ assertFloatingToolbarMenuItem(hasMenuItemId, false);
+ }
+
+ private static void assertFloatingToolbarMenuItem(
+ final Predicate<View> predicate, final boolean positiveAssertion) {
onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() {
@Override
public boolean matchesSafely(View view) {
- return !hasMenuItemWithSpecifiedId(view);
+ return positiveAssertion == containsItem(view);
}
@Override
public void describeTo(Description description) {}
- private boolean hasMenuItemWithSpecifiedId(View view) {
- if (view.getTag() instanceof MenuItem
- && ((MenuItem) view.getTag()).getItemId() == menuItemId) {
+ private boolean containsItem(View view) {
+ if (predicate.test(view)) {
return true;
} else if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
- if (hasMenuItemWithSpecifiedId(viewGroup.getChildAt(i))) {
+ if (containsItem(viewGroup.getChildAt(i))) {
return true;
}
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 2644292..24ce1e4 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -68,6 +68,7 @@
],
static_libs: [
"libplatformprotos",
+ "libEGL_blobCache",
],
}
@@ -131,6 +132,7 @@
"pipeline/skia/LayerDrawable.cpp",
"pipeline/skia/RenderNodeDrawable.cpp",
"pipeline/skia/ReorderBarrierDrawables.cpp",
+ "pipeline/skia/ShaderCache.cpp",
"pipeline/skia/SkiaDisplayList.cpp",
"pipeline/skia/SkiaOpenGLPipeline.cpp",
"pipeline/skia/SkiaOpenGLReadback.cpp",
@@ -346,6 +348,7 @@
"tests/unit/RecordingCanvasTests.cpp",
"tests/unit/RenderNodeTests.cpp",
"tests/unit/RenderPropertiesTests.cpp",
+ "tests/unit/ShaderCacheTests.cpp",
"tests/unit/SkiaBehaviorTests.cpp",
"tests/unit/SkiaDisplayListTests.cpp",
"tests/unit/SkiaPipelineTests.cpp",
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
new file mode 100644
index 0000000..87edd69
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+#include "ShaderCache.h"
+#include <algorithm>
+#include <log/log.h>
+#include <thread>
+#include "FileBlobCache.h"
+#include "utils/TraceUtils.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+// Cache size limits.
+static const size_t maxKeySize = 1024;
+static const size_t maxValueSize = 64 * 1024;
+static const size_t maxTotalSize = 512 * 1024;
+
+ShaderCache::ShaderCache() {
+ // There is an "incomplete FileBlobCache type" compilation error, if ctor is moved to header.
+}
+
+ShaderCache ShaderCache::sCache;
+
+ShaderCache& ShaderCache::get() {
+ return sCache;
+}
+
+void ShaderCache::initShaderDiskCache() {
+ ATRACE_NAME("initShaderDiskCache");
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mFilename.length() > 0) {
+ mBlobCache.reset(new FileBlobCache(maxKeySize, maxValueSize, maxTotalSize, mFilename));
+ mInitialized = true;
+ }
+}
+
+void ShaderCache::setFilename(const char* filename) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFilename = filename;
+}
+
+BlobCache* ShaderCache::getBlobCacheLocked() {
+ LOG_ALWAYS_FATAL_IF(!mInitialized, "ShaderCache has not been initialized");
+ return mBlobCache.get();
+}
+
+sk_sp<SkData> ShaderCache::load(const SkData& key) {
+ ATRACE_NAME("ShaderCache::load");
+ size_t keySize = key.size();
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mInitialized) {
+ ALOGE("ShaderCache::load not initialized");
+ return nullptr;
+ }
+
+ // mObservedBlobValueSize is reasonably big to avoid memory reallocation
+ // Allocate a buffer with malloc. SkData takes ownership of that allocation and will call free.
+ void* valueBuffer = malloc(mObservedBlobValueSize);
+ if (!valueBuffer) {
+ return nullptr;
+ }
+ BlobCache* bc = getBlobCacheLocked();
+ size_t valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize);
+ int maxTries = 3;
+ while (valueSize > mObservedBlobValueSize && maxTries > 0) {
+ mObservedBlobValueSize = std::min(valueSize, maxValueSize);
+ valueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
+ if (!valueBuffer) {
+ return nullptr;
+ }
+ valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize);
+ maxTries--;
+ }
+ if (!valueSize) {
+ free(valueBuffer);
+ return nullptr;
+ }
+ if (valueSize > mObservedBlobValueSize) {
+ ALOGE("ShaderCache::load value size is too big %d", (int) valueSize);
+ free(valueBuffer);
+ return nullptr;
+ }
+ return SkData::MakeFromMalloc(valueBuffer, valueSize);
+}
+
+void ShaderCache::store(const SkData& key, const SkData& data) {
+ ATRACE_NAME("ShaderCache::store");
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (!mInitialized) {
+ ALOGE("ShaderCache::store not initialized");
+ return;
+ }
+
+ size_t valueSize = data.size();
+ size_t keySize = key.size();
+ if (keySize == 0 || valueSize == 0 || valueSize >= maxValueSize) {
+ ALOGW("ShaderCache::store: sizes %d %d not allowed", (int)keySize, (int)valueSize);
+ return;
+ }
+
+ const void* value = data.data();
+
+ BlobCache* bc = getBlobCacheLocked();
+ bc->set(key.data(), keySize, value, valueSize);
+
+ if (!mSavePending && mDeferredSaveDelay > 0) {
+ mSavePending = true;
+ std::thread deferredSaveThread([this]() {
+ sleep(mDeferredSaveDelay);
+ std::lock_guard<std::mutex> lock(mMutex);
+ ATRACE_NAME("ShaderCache::saveToDisk");
+ if (mInitialized && mBlobCache) {
+ mBlobCache->writeToFile();
+ }
+ mSavePending = false;
+ });
+ deferredSaveThread.detach();
+ }
+}
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
new file mode 100644
index 0000000..27473d6
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cutils/compiler.h>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+#include <GrContextOptions.h>
+
+namespace android {
+
+class BlobCache;
+class FileBlobCache;
+
+namespace uirenderer {
+namespace skiapipeline {
+
+class ShaderCache : public GrContextOptions::PersistentCache {
+public:
+ /**
+ * "get" returns a pointer to the singleton ShaderCache object. This
+ * singleton object will never be destroyed.
+ */
+ ANDROID_API static ShaderCache& get();
+
+ /**
+ * "initShaderDiskCache" loads the serialized cache contents from disk and puts the ShaderCache
+ * into an initialized state, such that it is able to insert and retrieve entries from the
+ * cache. This should be called when HWUI pipeline is initialized. When not in the initialized
+ * state the load and store methods will return without performing any cache operations.
+ */
+ virtual void initShaderDiskCache();
+
+ /**
+ * "setFilename" sets the name of the file that should be used to store
+ * cache contents from one program invocation to another. This function does not perform any
+ * disk operation and it should be invoked before "initShaderCache".
+ */
+ virtual void setFilename(const char* filename);
+
+ /**
+ * "load" attempts to retrieve the value blob associated with a given key
+ * blob from cache. This will be called by Skia, when it needs to compile a new SKSL shader.
+ */
+ sk_sp<SkData> load(const SkData& key) override;
+
+ /**
+ * "store" attempts to insert a new key/value blob pair into the cache.
+ * This will be called by Skia after it compiled a new SKSL shader
+ */
+ void store(const SkData& key, const SkData& data) override;
+
+private:
+ // Creation and (the lack of) destruction is handled internally.
+ ShaderCache();
+
+ // Copying is disallowed.
+ ShaderCache(const ShaderCache&) = delete;
+ void operator=(const ShaderCache&) = delete;
+
+ /**
+ * "getBlobCacheLocked" returns the BlobCache object being used to store the
+ * key/value blob pairs. If the BlobCache object has not yet been created,
+ * this will do so, loading the serialized cache contents from disk if
+ * possible.
+ */
+ BlobCache* getBlobCacheLocked();
+
+ /**
+ * "mInitialized" indicates whether the ShaderCache is in the initialized
+ * state. It is initialized to false at construction time, and gets set to
+ * true when initialize is called.
+ * When in this state, the cache behaves as normal. When not,
+ * the load and store methods will return without performing any cache
+ * operations.
+ */
+ bool mInitialized = false;
+
+ /**
+ * "mBlobCache" is the cache in which the key/value blob pairs are stored. It
+ * is initially NULL, and will be initialized by getBlobCacheLocked the
+ * first time it's needed.
+ * The blob cache contains the Android build number. We treat version mismatches as an empty
+ * cache (logic implemented in BlobCache::unflatten).
+ */
+ std::unique_ptr<FileBlobCache> mBlobCache;
+
+ /**
+ * "mFilename" is the name of the file for storing cache contents in between
+ * program invocations. It is initialized to an empty string at
+ * construction time, and can be set with the setCacheFilename method. An
+ * empty string indicates that the cache should not be saved to or restored
+ * from disk.
+ */
+ std::string mFilename;
+
+ /**
+ * "mSavePending" indicates whether or not a deferred save operation is
+ * pending. Each time a key/value pair is inserted into the cache via
+ * load, a deferred save is initiated if one is not already pending.
+ * This will wait some amount of time and then trigger a save of the cache
+ * contents to disk.
+ */
+ bool mSavePending = false;
+
+ /**
+ * "mObservedBlobValueSize" is the maximum value size observed by the cache reading function.
+ */
+ size_t mObservedBlobValueSize = 20*1024;
+
+ /**
+ * The time in seconds to wait before saving newly inserted cache entries.
+ */
+ unsigned int mDeferredSaveDelay = 4;
+
+ /**
+ * "mMutex" is the mutex used to prevent concurrent access to the member
+ * variables. It must be locked whenever the member variables are accessed.
+ */
+ mutable std::mutex mMutex;
+
+ /**
+ * "sCache" is the singleton ShaderCache object.
+ */
+ static ShaderCache sCache;
+
+ friend class ShaderCacheTestUtils; //used for unit testing
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index a33b287..c22364b 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -18,6 +18,7 @@
#include "Layer.h"
#include "RenderThread.h"
+#include "pipeline/skia/ShaderCache.h"
#include "renderstate/RenderState.h"
#include <GrContextOptions.h>
@@ -127,6 +128,8 @@
}
contextOptions->fExecutor = mTaskProcessor.get();
}
+
+ contextOptions->fPersistentCache = &skiapipeline::ShaderCache::get();
}
void CacheManager::trimMemory(TrimMemoryMode mode) {
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index c117cb8..574bb02 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -16,6 +16,7 @@
#include "RenderThread.h"
+#include "pipeline/skia/ShaderCache.h"
#include "CanvasContext.h"
#include "EglManager.h"
#include "OpenGLReadback.h"
@@ -105,6 +106,7 @@
mRenderState = new RenderState(*this);
mVkManager = new VulkanManager(*this);
mCacheManager = new CacheManager(mDisplayInfo);
+ uirenderer::skiapipeline::ShaderCache::get().initShaderDiskCache();
}
void RenderThread::dumpGraphicsMemory(int fd) {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 3272d69..1d8cdd6 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -107,8 +107,11 @@
mGetDeviceQueue(mBackendContext->fDevice, mPresentQueueIndex, 0, &mPresentQueue);
+ GrContextOptions options;
+ options.fDisableDistanceFieldPaths = true;
+ mRenderThread.cacheManager().configureContext(&options);
mRenderThread.setGrContext(
- GrContext::Create(kVulkan_GrBackend, (GrBackendContext)mBackendContext.get()));
+ GrContext::Create(kVulkan_GrBackend, (GrBackendContext)mBackendContext.get(), options));
DeviceInfo::initialize(mRenderThread.getGrContext()->caps()->maxRenderTargetSize());
if (Properties::enablePartialUpdates && Properties::useBufferAge) {
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
new file mode 100644
index 0000000..43080a9
--- /dev/null
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include <dirent.h>
+#include <cutils/properties.h>
+#include <cstdint>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+#include "pipeline/skia/ShaderCache.h"
+#include "FileBlobCache.h"
+
+using namespace android::uirenderer::skiapipeline;
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class ShaderCacheTestUtils {
+public:
+ /**
+ * "setSaveDelay" sets the time in seconds to wait before saving newly inserted cache entries.
+ * If set to 0, then deferred save is disabled.
+ */
+ static void setSaveDelay(ShaderCache& cache, unsigned int saveDelay) {
+ cache.mDeferredSaveDelay = saveDelay;
+ }
+
+ /**
+ * "terminate" optionally stores the BlobCache on disk and release all in-memory cache.
+ * Next call to "initShaderDiskCache" will load again the in-memory cache from disk.
+ */
+ static void terminate(ShaderCache& cache, bool saveContent) {
+ std::lock_guard<std::mutex> lock(cache.mMutex);
+ if (cache.mInitialized && cache.mBlobCache && saveContent) {
+ cache.mBlobCache->writeToFile();
+ }
+ cache.mBlobCache = NULL;
+ }
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
+
+
+namespace {
+
+std::string getExternalStorageFolder() {
+ return getenv("EXTERNAL_STORAGE");
+}
+
+bool folderExist(const std::string& folderName) {
+ DIR* dir = opendir(folderName.c_str());
+ if (dir) {
+ closedir(dir);
+ return true;
+ }
+ return false;
+}
+
+bool checkShader(const sk_sp<SkData>& shader, const char* program) {
+ sk_sp<SkData> shader2 = SkData::MakeWithCString(program);
+ return shader->size() == shader2->size()
+ && 0 == memcmp(shader->data(), shader2->data(), shader->size());
+}
+
+bool checkShader(const sk_sp<SkData>& shader, std::vector<char>& program) {
+ sk_sp<SkData> shader2 = SkData::MakeWithCopy(program.data(), program.size());
+ return shader->size() == shader2->size()
+ && 0 == memcmp(shader->data(), shader2->data(), shader->size());
+}
+
+void setShader(sk_sp<SkData>& shader, const char* program) {
+ shader = SkData::MakeWithCString(program);
+}
+
+void setShader(sk_sp<SkData>& shader, std::vector<char>& program) {
+ shader = SkData::MakeWithCopy(program.data(), program.size());
+}
+
+
+
+#define GrProgramDescTest(a) (*SkData::MakeWithCString(#a).get())
+
+TEST(ShaderCacheTest, testWriteAndRead) {
+ if (!folderExist(getExternalStorageFolder())) {
+ //don't run the test if external storage folder is not available
+ return;
+ }
+ std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
+ std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
+
+ //remove any test files from previous test run
+ int deleteFile = remove(cacheFile1.c_str());
+ ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
+
+ //read the cache from a file that does not exist
+ ShaderCache::get().setFilename(cacheFile1.c_str());
+ ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
+ ShaderCache::get().initShaderDiskCache();
+
+ //read a key - should not be found since the cache is empty
+ sk_sp<SkData> outVS;
+ ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
+
+ //write to the in-memory cache without storing on disk and verify we read the same values
+ sk_sp<SkData> inVS;
+ setShader(inVS, "sassas");
+ ShaderCache::get().store(GrProgramDescTest(100), *inVS.get());
+ setShader(inVS, "someVS");
+ ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
+ ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(100))), sk_sp<SkData>());
+ ASSERT_TRUE(checkShader(outVS, "sassas"));
+ ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
+ ASSERT_TRUE(checkShader(outVS, "someVS"));
+
+ //store content to disk and release in-memory cache
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
+
+ //change to a file that does not exist and verify load fails
+ ShaderCache::get().setFilename(cacheFile2.c_str());
+ ShaderCache::get().initShaderDiskCache();
+ ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
+
+ //load again content from disk from an existing file and check the data is read correctly
+ ShaderCache::get().setFilename(cacheFile1.c_str());
+ ShaderCache::get().initShaderDiskCache();
+ sk_sp<SkData> outVS2;
+ ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
+ ASSERT_TRUE(checkShader(outVS2, "someVS"));
+
+ //change data, store to disk, read back again and verify data has been changed
+ setShader(inVS, "ewData1");
+ ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
+ ShaderCache::get().initShaderDiskCache();
+ ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
+ ASSERT_TRUE(checkShader(outVS2, "ewData1"));
+
+
+ //write and read big data chunk (50K)
+ size_t dataSize = 50*1024;
+ std::vector<char> dataBuffer(dataSize);
+ for (size_t i = 0; i < dataSize; i++) {
+ dataBuffer[0] = dataSize % 256;
+ }
+ setShader(inVS, dataBuffer);
+ ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
+ ShaderCache::get().initShaderDiskCache();
+ ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
+ ASSERT_TRUE(checkShader(outVS2, dataBuffer));
+
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
+ remove(cacheFile1.c_str());
+}
+
+} // namespace
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 4487abc..3d9a2dc 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -34,44 +34,9 @@
android:id="@+id/volume_dialog_rows"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingEnd="@dimen/volume_button_size"
android:orientation="vertical" >
<!-- volume rows added and removed here! :-) -->
</LinearLayout>
-
- <include layout="@layout/volume_zen_footer" />
-
- <!-- Only shown from Tuner setting -->
- <include layout="@layout/tuner_zen_mode_panel" />
</LinearLayout>
- <LinearLayout
- android:id="@+id/volume_dialog_content"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_alignParentEnd="true"
- android:layout_alignParentTop="true"
- android:layout_marginEnd="@dimen/volume_expander_margin_end" >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="1"
- android:textAppearance="@style/TextAppearance.Volume.Header" />
- <com.android.keyguard.AlphaOptimizedImageButton
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/volume_expand_button"
- style="@style/VolumeButtons"
- android:layout_width="@dimen/volume_button_size"
- android:layout_height="@dimen/volume_button_size"
- android:clickable="true"
- android:soundEffectsEnabled="false"
- android:src="@drawable/ic_volume_collapse_animation"
- android:background="@drawable/ripple_drawable"
- tools:ignore="RtlHardcoded"
- />
-
- </LinearLayout>
</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/volume_zen_footer.xml b/packages/SystemUI/res/layout/volume_zen_footer.xml
deleted file mode 100644
index df79c5f..0000000
--- a/packages/SystemUI/res/layout/volume_zen_footer.xml
+++ /dev/null
@@ -1,127 +0,0 @@
-<!--
- Copyright (C) 2015 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.
--->
-<com.android.systemui.volume.ZenFooter xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/volume_zen_footer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingBottom="8dp" > <!-- extends LinearLayout -->
-
- <View
- android:id="@+id/zen_embedded_divider"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:layout_marginTop="8dp"
- android:background="@color/qs_tile_divider" />
-
- <RelativeLayout
- android:id="@+id/zen_introduction"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp"
- android:paddingBottom="8dp"
- android:background="@drawable/zen_introduction_message_background"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent.Light">
-
- <ImageView
- android:id="@+id/zen_introduction_confirm"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_marginEnd="8dp"
- android:layout_alignParentEnd="true"
- android:background="@drawable/btn_borderless_rect"
- android:clickable="true"
- android:contentDescription="@string/accessibility_desc_close"
- android:scaleType="center"
- android:src="@drawable/ic_close_white_rounded" />
-
- <TextView
- android:id="@+id/zen_introduction_message"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="12dp"
- android:layout_marginStart="24dp"
- android:textDirection="locale"
- android:lineSpacingMultiplier="1.20029"
- android:layout_toStartOf="@id/zen_introduction_confirm"
- android:text="@string/zen_alarms_introduction"
- android:textAppearance="@style/TextAppearance.QS.Introduction" />
-
- <View
- android:layout_width="0dp"
- android:layout_height="16dp"
- android:layout_below="@id/zen_introduction_message"
- android:layout_alignParentEnd="true" />
-
- </RelativeLayout>
-
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:orientation="horizontal" >
-
- <ImageView
- android:id="@+id/volume_zen_icon"
- android:layout_width="@dimen/volume_button_size"
- android:layout_height="@dimen/volume_button_size"
- android:layout_marginEnd="7dp"
- android:scaleType="center" />
-
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical" >
-
- <TextView
- android:id="@+id/volume_zen_summary_line_1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textDirection="locale"
- android:textAppearance="@style/TextAppearance.Volume.ZenSummary" />
-
- <TextView
- android:id="@+id/volume_zen_summary_line_2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="1dp"
- android:textDirection="locale"
- android:textAppearance="@style/TextAppearance.Volume.ZenDetail" />
-
- </LinearLayout>
-
- </LinearLayout>
-
- <TextView
- android:id="@+id/volume_zen_end_now"
- style="@style/QSBorderlessButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end"
- android:layout_marginEnd="8dp"
- android:clickable="true"
- android:focusable="true"
- android:paddingStart="15dp"
- android:paddingEnd="15dp"
- android:text="@string/volume_zen_end_now"
- android:textColor="?android:attr/colorAccent"
- android:textAppearance="@style/TextAppearance.QS.DetailButton" />
-
-</com.android.systemui.volume.ZenFooter>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 32f502b..46ea494 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -105,13 +105,6 @@
android:title="@string/volume_and_do_not_disturb">
<!-- Action for this is
- MetricsConstants.ACTION_TUNER_DO_NOT_DISTURB_VOLUME_PANEL -->
- <com.android.systemui.tuner.TunerSwitch
- android:key="sysui_show_full_zen"
- android:title="@string/tuner_full_zen_title"
- sysui:metricsAction="314" />
-
- <!-- Action for this is
MetricsConstants.ACTION_TUNER_DO_NOT_DISTURB_VOLUME_SHORTCUT -->
<com.android.systemui.tuner.TunerSwitch
android:key="sysui_volume_down_silent,sysui_volume_up_silent"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 532ead1..5ffd785 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -338,7 +338,9 @@
final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
- mMultiUserSwitch.setVisibility(mExpanded && mMultiUserSwitch.hasMultipleUsers() && !isDemo
+
+ mMultiUserSwitch.setVisibility(mExpanded
+ && UserManager.get(mContext).isUserSwitcherEnabled()
? View.VISIBLE : View.INVISIBLE);
mEdit.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index f6d36e8..ee8f18e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -96,7 +96,6 @@
private VolumeDialog createDefault() {
VolumeDialogImpl impl = new VolumeDialogImpl(mContext);
- impl.setStreamImportant(AudioManager.STREAM_ALARM, true);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
impl.setAutomute(true);
impl.setSilentMode(false);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 761e979..1ecaa13 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -26,16 +26,13 @@
import android.app.Dialog;
import android.app.KeyguardManager;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.os.Debug;
@@ -44,9 +41,6 @@
import android.os.Message;
import android.os.SystemClock;
import android.provider.Settings.Global;
-import android.transition.AutoTransition;
-import android.transition.Transition;
-import android.transition.TransitionManager;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Slog;
@@ -74,16 +68,12 @@
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.plugins.VolumeDialog;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.VolumeDialogController.State;
import com.android.systemui.plugins.VolumeDialogController.StreamState;
import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerZenModePanel;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -96,7 +86,7 @@
*
* Methods ending in "H" must be called on the (ui) handler.
*/
-public class VolumeDialogImpl implements VolumeDialog, TunerService.Tunable {
+public class VolumeDialogImpl implements VolumeDialog {
private static final String TAG = Util.logTag(VolumeDialogImpl.class);
public static final String SHOW_FULL_ZEN = "sysui_show_full_zen";
@@ -113,7 +103,6 @@
private ViewGroup mDialogView;
private ViewGroup mDialogRowsView;
private ViewGroup mDialogContentView;
- private ImageButton mExpandButton;
private final List<VolumeRow> mRows = new ArrayList<>();
private ConfigurableTexts mConfigurableTexts;
private final SparseBooleanArray mDynamic = new SparseBooleanArray();
@@ -121,7 +110,6 @@
private final AudioManager mAudioManager;
private final AccessibilityManager mAccessibilityMgr;
private int mExpandButtonAnimationDuration;
- private ZenFooter mZenFooter;
private final Object mSafetyWarningLock = new Object();
private final Accessibility mAccessibility = new Accessibility();
private final ColorStateList mActiveSliderTint;
@@ -131,7 +119,6 @@
private final ZenModeController mZenModeController;
private boolean mShowing;
- private boolean mExpanded;
private boolean mShowA11yStream;
private int mActiveStream;
@@ -139,7 +126,6 @@
private boolean mAutomute = VolumePrefs.DEFAULT_ENABLE_AUTOMUTE;
private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE;
private State mState;
- private boolean mExpandButtonAnimationRunning;
private SafetyWarningDialog mSafetyWarning;
private Callback mCallback;
private boolean mPendingStateChanged;
@@ -148,9 +134,6 @@
private boolean mHovering = false;
private int mDensity;
- private boolean mShowFullZen;
- private TunerZenModePanel mZenPanel;
-
public VolumeDialogImpl(Context context) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
mZenModeController = Dependency.get(ZenModeController.class);
@@ -173,7 +156,6 @@
mController.addCallback(mControllerCallbackH, mHandler);
mController.getState();
- Dependency.get(TunerService.class).addTunable(this, SHOW_FULL_ZEN);
final Configuration currentConfig = mContext.getResources().getConfiguration();
mDensity = currentConfig.densityDpi;
@@ -183,10 +165,6 @@
public void destroy() {
mAccessibility.destroy();
mController.removeCallback(mControllerCallbackH);
- if (mZenFooter != null) {
- mZenFooter.cleanup();
- }
- Dependency.get(TunerService.class).removeTunable(this);
mHandler.removeCallbacksAndMessages(null);
}
@@ -234,16 +212,9 @@
mDialogContentView = mDialog.findViewById(R.id.volume_dialog_content);
mDialogRowsView = mDialogContentView.findViewById(R.id.volume_dialog_rows);
- mExpanded = false;
- mExpandButton = (ImageButton) mDialogView.findViewById(R.id.volume_expand_button);
- mExpandButton.setOnClickListener(mClickExpand);
-
- mExpandButton.setVisibility(
- AudioSystem.isSingleVolume(mContext) ? View.GONE : View.VISIBLE);
updateWindowWidthH();
- updateExpandButtonH();
- mMotion = new VolumeDialogMotion(mDialog, mDialogView, mDialogContentView, mExpandButton,
+ mMotion = new VolumeDialogMotion(mDialog, mDialogView, mDialogContentView,
new VolumeDialogMotion.Callback() {
@Override
public void onAnimatingChanged(boolean animating) {
@@ -280,18 +251,6 @@
addExistingRows();
}
mExpandButtonAnimationDuration = res.getInteger(R.integer.volume_expand_animation_duration);
- mZenFooter = (ZenFooter) mDialog.findViewById(R.id.volume_zen_footer);
- mZenFooter.init(mZenModeController);
- mZenPanel = (TunerZenModePanel) mDialog.findViewById(R.id.tuner_zen_mode_panel);
- mZenPanel.init(mZenModeController);
- mZenPanel.setCallback(mZenPanelCallback);
- }
-
- @Override
- public void onTuningChanged(String key, String newValue) {
- if (SHOW_FULL_ZEN.equals(key)) {
- mShowFullZen = newValue != null && Integer.parseInt(newValue) != 0;
- }
}
private ColorStateList loadColorStateList(int colorResId) {
@@ -359,11 +318,6 @@
}
}
-
- private boolean isAttached() {
- return mDialogContentView != null && mDialogContentView.isAttachedToWindow();
- }
-
private VolumeRow getActiveRow() {
for (VolumeRow row : mRows) {
if (row.stream == mActiveStream) {
@@ -383,9 +337,6 @@
public void dump(PrintWriter writer) {
writer.println(VolumeDialogImpl.class.getSimpleName() + " state:");
writer.print(" mShowing: "); writer.println(mShowing);
- writer.print(" mExpanded: "); writer.println(mExpanded);
- writer.print(" mExpandButtonAnimationRunning: ");
- writer.println(mExpandButtonAnimationRunning);
writer.print(" mActiveStream: "); writer.println(mActiveStream);
writer.print(" mDynamic: "); writer.println(mDynamic);
writer.print(" mAutomute: "); writer.println(mAutomute);
@@ -514,11 +465,7 @@
if (mAccessibility.mFeedbackEnabled) return 20000;
if (mHovering) return 16000;
if (mSafetyWarning != null) return 5000;
- if (mExpanded || mExpandButtonAnimationRunning) return 5000;
if (mActiveStream == AudioManager.STREAM_MUSIC) return 1500;
- if (mZenFooter.shouldShowIntroduction()) {
- return 6000;
- }
return 3000;
}
@@ -530,12 +477,7 @@
mHandler.removeMessages(H.SHOW);
if (!mShowing) return;
mShowing = false;
- mMotion.startDismiss(new Runnable() {
- @Override
- public void run() {
- updateExpandedH(false /* expanding */, true /* dismissing */);
- }
- });
+ mMotion.startDismiss();
if (mAccessibilityMgr.isEnabled()) {
AccessibilityEvent event =
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
@@ -579,59 +521,6 @@
mHandler.sendEmptyMessageDelayed(H.UPDATE_BOTTOM_MARGIN, getConservativeCollapseDuration());
}
- private void updateExpandedH(final boolean expanded, final boolean dismissing) {
- if (mExpanded == expanded) return;
- mExpanded = expanded;
- mExpandButtonAnimationRunning = isAttached();
- if (D.BUG) Log.d(TAG, "updateExpandedH " + expanded);
- updateExpandButtonH();
- updateFooterH();
- TransitionManager.endTransitions(mDialogView);
- final VolumeRow activeRow = getActiveRow();
- if (!dismissing) {
- mWindow.setLayout(mWindow.getAttributes().width, ViewGroup.LayoutParams.MATCH_PARENT);
- TransitionManager.beginDelayedTransition(mDialogView, getTransition());
- }
- updateRowsH(activeRow);
- rescheduleTimeoutH();
- }
-
- private void updateExpandButtonH() {
- if (D.BUG) Log.d(TAG, "updateExpandButtonH");
- mExpandButton.setClickable(!mExpandButtonAnimationRunning);
- if (!(mExpandButtonAnimationRunning && isAttached())) {
- final int res = mExpanded ? R.drawable.ic_volume_collapse_animation
- : R.drawable.ic_volume_expand_animation;
- if (hasTouchFeature()) {
- mExpandButton.setImageResource(res);
- } else {
- // if there is no touch feature, show the volume ringer instead
- mExpandButton.setImageResource(R.drawable.ic_volume_ringer);
- mExpandButton.setBackgroundResource(0); // remove gray background emphasis
- }
- mExpandButton.setContentDescription(mContext.getString(mExpanded ?
- R.string.accessibility_volume_collapse : R.string.accessibility_volume_expand));
- }
- if (mExpandButtonAnimationRunning) {
- final Drawable d = mExpandButton.getDrawable();
- if (d instanceof AnimatedVectorDrawable) {
- // workaround to reset drawable
- final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) d.getConstantState()
- .newDrawable();
- mExpandButton.setImageDrawable(avd);
- avd.start();
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- mExpandButtonAnimationRunning = false;
- updateExpandButtonH();
- rescheduleTimeoutH();
- }
- }, mExpandButtonAnimationDuration);
- }
- }
- }
-
private boolean shouldBeVisibleH(VolumeRow row, VolumeRow activeRow) {
boolean isActive = row == activeRow;
if (row.stream == AudioSystem.STREAM_ACCESSIBILITY) {
@@ -639,15 +528,13 @@
}
// if the active row is accessibility, then continue to display previous
- // active row since accessibility is dispalyed under it
+ // active row since accessibility is displayed under it
if (activeRow.stream == AudioSystem.STREAM_ACCESSIBILITY &&
row.stream == mPrevActiveStream) {
return true;
}
- return mExpanded && row.view.getVisibility() == View.VISIBLE
- || (mExpanded && (row.important || isActive))
- || !mExpanded && isActive;
+ return row.important || isActive;
}
private void updateRowsH(final VolumeRow activeRow) {
@@ -709,38 +596,6 @@
for (VolumeRow row : mRows) {
updateVolumeRowH(row);
}
- updateFooterH();
- }
-
- private void updateFooterH() {
- if (D.BUG) Log.d(TAG, "updateFooterH");
- final boolean wasVisible = mZenFooter.getVisibility() == View.VISIBLE;
- final boolean visible = mState.zenMode != Global.ZEN_MODE_OFF
- && (mAudioManager.isStreamAffectedByRingerMode(mActiveStream) || mExpanded)
- && !mZenPanel.isEditing();
-
- TransitionManager.endTransitions(mDialogView);
- TransitionManager.beginDelayedTransition(mDialogView, getTransition());
- if (wasVisible != visible && !visible) {
- prepareForCollapse();
- }
- Util.setVisOrGone(mZenFooter, visible);
- mZenFooter.update();
-
- final boolean fullWasVisible = mZenPanel.getVisibility() == View.VISIBLE;
- final boolean fullVisible = mShowFullZen && !visible;
- if (fullWasVisible != fullVisible) {
- Util.setVisOrGone(mZenPanel, fullVisible);
- if (fullVisible) {
- mZenPanel.setZenState(mState.zenMode);
- mZenPanel.setDoneListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mHandler.sendEmptyMessage(H.UPDATE_FOOTER);
- }
- });
- }
- }
}
private void updateVolumeRowH(VolumeRow row) {
@@ -860,7 +715,7 @@
}
private void updateVolumeRowSliderTintH(VolumeRow row, boolean isActive) {
- if (isActive && mExpanded) {
+ if (isActive) {
row.slider.requestFocus();
}
final ColorStateList tint = isActive && row.slider.isEnabled() ? mActiveSliderTint
@@ -980,43 +835,6 @@
}
}
- private AutoTransition getTransition() {
- AutoTransition transition = new AutoTransition();
- transition.setDuration(mExpandButtonAnimationDuration);
- transition.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- transition.addListener(new Transition.TransitionListener() {
- @Override
- public void onTransitionStart(Transition transition) {
- }
-
- @Override
- public void onTransitionEnd(Transition transition) {
- mWindow.setLayout(
- mWindow.getAttributes().width, ViewGroup.LayoutParams.WRAP_CONTENT);
- }
-
- @Override
- public void onTransitionCancel(Transition transition) {
- }
-
- @Override
- public void onTransitionPause(Transition transition) {
- mWindow.setLayout(
- mWindow.getAttributes().width, ViewGroup.LayoutParams.WRAP_CONTENT);
- }
-
- @Override
- public void onTransitionResume(Transition transition) {
- }
- });
- return transition;
- }
-
- private boolean hasTouchFeature() {
- final PackageManager pm = mContext.getPackageManager();
- return pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
- }
-
private final VolumeDialogController.Callbacks mControllerCallbackH
= new VolumeDialogController.Callbacks() {
@Override
@@ -1050,13 +868,11 @@
final int density = newConfig.densityDpi;
if (density != mDensity) {
mDialog.dismiss();
- mZenFooter.cleanup();
initDialog();
mDensity = density;
}
updateWindowWidthH();
mConfigurableTexts.update();
- mZenFooter.onConfigurationChanged();
}
@Override
@@ -1109,16 +925,6 @@
}
};
- private final OnClickListener mClickExpand = new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mExpandButtonAnimationRunning) return;
- final boolean newExpand = !mExpanded;
- Events.writeEvent(mContext, Events.EVENT_EXPAND, newExpand);
- updateExpandedH(newExpand, false /* dismissing */);
- }
- };
-
private final class H extends Handler {
private static final int SHOW = 1;
private static final int DISMISS = 2;
@@ -1128,7 +934,6 @@
private static final int RESCHEDULE_TIMEOUT = 6;
private static final int STATE_CHANGED = 7;
private static final int UPDATE_BOTTOM_MARGIN = 8;
- private static final int UPDATE_FOOTER = 9;
public H() {
super(Looper.getMainLooper());
@@ -1145,7 +950,6 @@
case RESCHEDULE_TIMEOUT: rescheduleTimeoutH(); break;
case STATE_CHANGED: onStateChangedH(mState); break;
case UPDATE_BOTTOM_MARGIN: updateDialogBottomMarginH(); break;
- case UPDATE_FOOTER: updateFooterH(); break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java
index 01d31e2..2b65fbd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java
@@ -25,7 +25,6 @@
import android.content.DialogInterface;
import android.content.DialogInterface.OnDismissListener;
import android.content.DialogInterface.OnShowListener;
-import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.util.Log;
import android.view.View;
@@ -41,22 +40,19 @@
private final Dialog mDialog;
private final View mDialogView;
private final ViewGroup mContents; // volume rows + zen footer
- private final View mChevron;
private final Handler mHandler = new Handler();
private final Callback mCallback;
private boolean mAnimating; // show or dismiss animation is running
private boolean mShowing; // show animation is running
private boolean mDismissing; // dismiss animation is running
- private ValueAnimator mChevronPositionAnimator;
private ValueAnimator mContentsPositionAnimator;
- public VolumeDialogMotion(Dialog dialog, View dialogView, ViewGroup contents, View chevron,
+ public VolumeDialogMotion(Dialog dialog, View dialogView, ViewGroup contents,
Callback callback) {
mDialog = dialog;
mDialogView = dialogView;
mContents = contents;
- mChevron = chevron;
mCallback = callback;
mDialog.setOnDismissListener(new OnDismissListener() {
@Override
@@ -117,15 +113,6 @@
mDialog.show();
}
- private int chevronDistance() {
- return mChevron.getHeight() / 6;
- }
-
- private int chevronPosY() {
- final Object tag = mChevron == null ? null : mChevron.getTag();
- return tag == null ? 0 : (Integer) tag;
- }
-
private void startShowAnimation() {
if (D.BUG) Log.d(TAG, "startShowAnimation");
mDialogView.animate()
@@ -133,28 +120,9 @@
.setDuration(scaledDuration(300))
.setInterpolator(new LogDecelerateInterpolator())
.setListener(null)
- .setUpdateListener(animation -> {
- if (mChevronPositionAnimator != null) {
- final float v = (Float) mChevronPositionAnimator.getAnimatedValue();
- if (mChevronPositionAnimator == null) return;
- // reposition chevron
- final int posY = chevronPosY();
- mChevron.setTranslationY(posY + v + -mDialogView.getTranslationY());
- }
- })
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- if (mChevronPositionAnimator == null) return;
- // reposition chevron
- final int posY = chevronPosY();
- mChevron.setTranslationY(posY + -mDialogView.getTranslationY());
- }
- })
.start();
- mContentsPositionAnimator = ValueAnimator.ofFloat(-chevronDistance(), 0)
- .setDuration(scaledDuration(400));
+ mContentsPositionAnimator = ValueAnimator.ofFloat(0).setDuration(scaledDuration(400));
mContentsPositionAnimator.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled;
@@ -186,22 +154,9 @@
.setDuration(scaledDuration(150))
.setInterpolator(new PathInterpolator(0f, 0f, .2f, 1f))
.start();
-
- mChevronPositionAnimator = ValueAnimator.ofFloat(-chevronDistance(), 0)
- .setDuration(scaledDuration(250));
- mChevronPositionAnimator.setInterpolator(new PathInterpolator(.4f, 0f, .2f, 1f));
- mChevronPositionAnimator.start();
-
- mChevron.setAlpha(0);
- mChevron.animate()
- .alpha(1)
- .setStartDelay(scaledDuration(50))
- .setDuration(scaledDuration(150))
- .setInterpolator(new PathInterpolator(.4f, 0f, 1f, 1f))
- .start();
}
- public void startDismiss(final Runnable onComplete) {
+ public void startDismiss() {
if (D.BUG) Log.d(TAG, "startDismiss");
if (mDismissing) return;
setDismissing(true);
@@ -211,10 +166,6 @@
mContentsPositionAnimator.cancel();
}
mContents.animate().cancel();
- if (mChevronPositionAnimator != null) {
- mChevronPositionAnimator.cancel();
- }
- mChevron.animate().cancel();
setShowing(false);
}
mDialogView.animate()
@@ -225,8 +176,6 @@
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mContents.setTranslationY(-mDialogView.getTranslationY());
- final int posY = chevronPosY();
- mChevron.setTranslationY(posY + -mDialogView.getTranslationY());
}
})
.setListener(new AnimatorListenerAdapter() {
@@ -240,7 +189,6 @@
public void run() {
if (D.BUG) Log.d(TAG, "mDialog.dismiss()");
mDialog.dismiss();
- onComplete.run();
setDismissing(false);
}
}, PRE_DISMISS_DELAY);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
deleted file mode 100644
index 80e1629..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2015 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.volume;
-
-import android.animation.LayoutTransition;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.provider.Settings.Global;
-import android.service.notification.ZenModeConfig;
-import android.transition.AutoTransition;
-import android.transition.TransitionManager;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.systemui.Prefs;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-import java.util.Objects;
-
-/**
- * Zen mode information (and end button) attached to the bottom of the volume dialog.
- */
-public class ZenFooter extends LinearLayout {
- private static final String TAG = Util.logTag(ZenFooter.class);
-
- private final Context mContext;
- private final ConfigurableTexts mConfigurableTexts;
-
- private ImageView mIcon;
- private TextView mSummaryLine1;
- private TextView mSummaryLine2;
- private TextView mEndNowButton;
- private View mZenIntroduction;
- private View mZenIntroductionConfirm;
- private TextView mZenIntroductionMessage;
- private int mZen = -1;
- private ZenModeConfig mConfig;
- private ZenModeController mController;
-
- public ZenFooter(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
- mConfigurableTexts = new ConfigurableTexts(mContext);
- final LayoutTransition layoutTransition = new LayoutTransition();
- layoutTransition.setDuration(new ValueAnimator().getDuration() / 2);
- setLayoutTransition(layoutTransition);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mIcon = findViewById(R.id.volume_zen_icon);
- mSummaryLine1 = findViewById(R.id.volume_zen_summary_line_1);
- mSummaryLine2 = findViewById(R.id.volume_zen_summary_line_2);
- mEndNowButton = findViewById(R.id.volume_zen_end_now);
- mZenIntroduction = findViewById(R.id.zen_introduction);
- mZenIntroductionMessage = findViewById(R.id.zen_introduction_message);
- mConfigurableTexts.add(mZenIntroductionMessage, R.string.zen_alarms_introduction);
- mZenIntroductionConfirm = findViewById(R.id.zen_introduction_confirm);
- mZenIntroductionConfirm.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- confirmZenIntroduction();
- }
- });
- Util.setVisOrGone(mZenIntroduction, shouldShowIntroduction());
- mConfigurableTexts.add(mSummaryLine1);
- mConfigurableTexts.add(mSummaryLine2);
- mConfigurableTexts.add(mEndNowButton, R.string.volume_zen_end_now);
- }
-
- public void init(final ZenModeController controller) {
- mEndNowButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- setZen(Global.ZEN_MODE_OFF);
- controller.setZen(Global.ZEN_MODE_OFF, null, TAG);
- }
- });
- mZen = controller.getZen();
- mConfig = controller.getConfig();
- mController = controller;
- mController.addCallback(mZenCallback);
- update();
- updateIntroduction();
- }
-
- public void cleanup() {
- mController.removeCallback(mZenCallback);
- }
-
- private void setZen(int zen) {
- if (mZen == zen) return;
- mZen = zen;
- update();
- post(() -> {
- updateIntroduction();
- });
- }
-
- private void setConfig(ZenModeConfig config) {
- if (Objects.equals(mConfig, config)) return;
- mConfig = config;
- update();
- }
-
- private void confirmZenIntroduction() {
- Prefs.putBoolean(mContext, Prefs.Key.DND_CONFIRMED_ALARM_INTRODUCTION, true);
- updateIntroduction();
- }
-
- private boolean isZenPriority() {
- return mZen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- }
-
- private boolean isZenAlarms() {
- return mZen == Global.ZEN_MODE_ALARMS;
- }
-
- private boolean isZenNone() {
- return mZen == Global.ZEN_MODE_NO_INTERRUPTIONS;
- }
-
- public void update() {
- mIcon.setImageResource(isZenNone() ? R.drawable.ic_dnd_total_silence : R.drawable.ic_dnd);
- final String line1 =
- isZenPriority() ? mContext.getString(R.string.interruption_level_priority)
- : isZenAlarms() ? mContext.getString(R.string.interruption_level_alarms)
- : isZenNone() ? mContext.getString(R.string.interruption_level_none)
- : null;
- Util.setText(mSummaryLine1, line1);
-
- final CharSequence line2 = ZenModeConfig.getConditionSummary(mContext, mConfig,
- mController.getCurrentUser(), true /*shortVersion*/);
- Util.setText(mSummaryLine2, line2);
- }
-
- public boolean shouldShowIntroduction() {
- final boolean confirmed = Prefs.getBoolean(mContext,
- Prefs.Key.DND_CONFIRMED_ALARM_INTRODUCTION, false);
- return !confirmed && isZenAlarms();
- }
-
- public void updateIntroduction() {
- Util.setVisOrGone(mZenIntroduction, shouldShowIntroduction());
- }
-
- public void onConfigurationChanged() {
- mConfigurableTexts.update();
- }
-
- private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
- @Override
- public void onZenChanged(int zen) {
- setZen(zen);
- }
- @Override
- public void onConfigChanged(ZenModeConfig config) {
- setConfig(config);
- }
- };
-}
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 67fae5b..e74736a 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -43,6 +43,9 @@
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
+ <uses-permission android:name="android.permission.STATUS_BAR" />
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+ <uses-permission android:name="android.permission.REAL_GET_TASKS" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 65d9699..fbcbd20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -23,6 +23,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;
+import android.os.ParcelFileDescriptor;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.testing.LeakCheck;
@@ -34,6 +35,8 @@
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.io.FileInputStream;
+import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@@ -84,6 +87,16 @@
return mContext;
}
+ protected void runShellCommand(String command) throws IOException {
+ ParcelFileDescriptor pfd = mRealInstrumentation.getUiAutomation()
+ .executeShellCommand(command);
+
+ // Read the input stream fully.
+ FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ while (fis.read() != -1);
+ fis.close();
+ }
+
protected void waitForIdleSync() {
if (mHandler == null) {
mHandler = new Handler(Looper.getMainLooper());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTest.java
new file mode 100644
index 0000000..bdbd244
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.recents;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+
+import static com.android.systemui.recents.RecentsImpl.RECENTS_ACTIVITY;
+import static com.android.systemui.recents.RecentsImpl.RECENTS_PACKAGE;
+
+import static org.junit.Assert.fail;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.IActivityManager;
+import android.os.SystemClock;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class RecentsTest extends SysuiTestCase {
+
+ @Test
+ public void testRecentsActivityType() throws Exception {
+ // Clear the state
+ final IActivityManager am = ActivityManager.getService();
+ am.removeStacksWithActivityTypes(new int[] { ACTIVITY_TYPE_RECENTS });
+
+ // Toggle recents, use a shell command because it is not exported
+ runShellCommand("am start -n " + RECENTS_PACKAGE + "/" + RECENTS_ACTIVITY);
+
+ // Verify that an activity was launched with the right activity type
+ int retryCount = 0;
+ while (retryCount < 10) {
+ List<RunningTaskInfo> tasks = am.getTasks(Integer.MAX_VALUE);
+ for (RunningTaskInfo info : tasks) {
+ if (info.configuration.windowConfiguration.getActivityType()
+ == ACTIVITY_TYPE_RECENTS) {
+ // Found a recents activity with the right activity type
+ return;
+ }
+ }
+ SystemClock.sleep(50);
+ retryCount++;
+ }
+ fail("Expected Recents activity with ACTIVITY_TYPE_RECENTS");
+ }
+}
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 06c110d..d661754 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1318,7 +1318,7 @@
private void unbindAllServicesLocked(UserState userState) {
List<AccessibilityServiceConnection> services = userState.mBoundServices;
- for (int count = services.size(); count > 1; count--) {
+ for (int count = services.size(); count > 0; count--) {
// When the service is unbound, it disappears from the list, so there's no need to
// keep track of the index
services.get(0).unbindLocked();
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index d8eaccc..a3def14 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -602,7 +602,7 @@
if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
mEventHistory.addEvent(
new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null,
- null, null, null, null));
+ null, null, null, null, null, -1));
}
}
}
@@ -616,7 +616,7 @@
if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) {
mEventHistory.addEvent(
new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
- clientState, null, null, null, null, null, null));
+ clientState, null, null, null, null, null, null, null, -1));
}
}
}
@@ -628,7 +628,7 @@
synchronized (mLock) {
if (isValidEventLocked("logSaveShown()", sessionId)) {
mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null,
- null, null, null, null, null));
+ null, null, null, null, null, null, -1));
}
}
}
@@ -642,7 +642,7 @@
if (isValidEventLocked("logDatasetSelected()", sessionId)) {
mEventHistory.addEvent(
new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null,
- null, null, null, null, null));
+ null, null, null, null, null, null, -1));
}
}
}
@@ -656,13 +656,15 @@
@Nullable ArrayList<AutofillId> changedFieldIds,
@Nullable ArrayList<String> changedDatasetIds,
@Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
- @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds) {
+ @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
+ @Nullable String detectedRemoteId, int detectedFieldScore) {
synchronized (mLock) {
if (isValidEventLocked("logDatasetNotSelected()", sessionId)) {
mEventHistory.addEvent(new Event(Event.TYPE_CONTEXT_COMMITTED, null,
clientState, selectedDatasets, ignoredDatasets,
changedFieldIds, changedDatasetIds,
- manuallyFilledFieldIds, manuallyFilledDatasetIds));
+ manuallyFilledFieldIds, manuallyFilledDatasetIds,
+ detectedRemoteId, detectedFieldScore));
}
}
}
@@ -695,6 +697,7 @@
pw.print(prefix); pw.print("Default component: ");
pw.println(mContext.getString(R.string.config_defaultAutofillService));
pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
+ pw.print(prefix); pw.print("Field detection: "); pw.println(isFieldDetectionEnabled());
pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
@@ -935,6 +938,13 @@
return false;
}
+ // TODO(b/67867469): remove once feature is finished
+ boolean isFieldDetectionEnabled() {
+ return Settings.Secure.getIntForUser(
+ mContext.getContentResolver(), Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION, 0,
+ mUserId) == 1;
+ }
+
@Override
public String toString() {
return "AutofillManagerServiceImpl: [userId=" + mUserId
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3564432..5823ab1 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -54,6 +54,7 @@
import android.os.SystemClock;
import android.service.autofill.AutofillService;
import android.service.autofill.Dataset;
+import android.service.autofill.FieldsDetection;
import android.service.autofill.FillContext;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
@@ -492,6 +493,13 @@
}
}
+ // TODO(b/67867469): remove once feature is finished
+ if (response.getFieldsDetection() != null && !mService.isFieldDetectionEnabled()) {
+ Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
+ processNullResponseLocked(requestFlags);
+ return;
+ }
+
mService.setLastResponse(serviceUid, id, response);
int sessionFinishedState = 0;
@@ -913,11 +921,29 @@
}
}
}
- if (!hasAtLeastOneDataset) {
- if (sVerbose) Slog.v(TAG, "logContextCommittedLocked(): skipped (no datasets)");
+ final FieldsDetection fieldsDetection = lastResponse.getFieldsDetection();
+
+ if (!hasAtLeastOneDataset && fieldsDetection == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "logContextCommittedLocked(): skipped (no datasets nor fields "
+ + "detection)");
+ }
return;
}
+ final AutofillId detectableFieldId;
+ final String detectableRemoteId;
+ String detectedRemoteId = null;
+ if (fieldsDetection == null) {
+ detectableFieldId = null;
+ detectableRemoteId = null;
+ } else {
+ detectableFieldId = fieldsDetection.getFieldId();
+ detectableRemoteId = fieldsDetection.getRemoteId();
+ }
+
+ int detectedFieldScore = -1;
+
for (int i = 0; i < mViewStates.size(); i++) {
final ViewState viewState = mViewStates.valueAt(i);
final int state = viewState.getState();
@@ -926,7 +952,6 @@
// - autofilled -> changedDatasetIds
// - not autofilled but matches a dataset value -> manuallyFilledIds
if ((state & ViewState.STATE_CHANGED) != 0) {
-
// Check if autofilled value was changed
if ((state & ViewState.STATE_AUTOFILLED) != 0) {
final String datasetId = viewState.getDatasetId();
@@ -958,7 +983,6 @@
changedFieldIds.add(viewState.id);
changedDatasetIds.add(datasetId);
} else {
- // Check if value match a dataset.
final AutofillValue currentValue = viewState.getCurrentValue();
if (currentValue == null) {
if (sDebug) {
@@ -967,58 +991,78 @@
}
continue;
}
- for (int j = 0; j < responseCount; j++) {
- final FillResponse response = mResponses.valueAt(j);
- final List<Dataset> datasets = response.getDatasets();
- if (datasets == null || datasets.isEmpty()) {
- if (sVerbose) Slog.v(TAG, "logContextCommitted() no datasets at " + j);
- } else {
- for (int k = 0; k < datasets.size(); k++) {
- final Dataset dataset = datasets.get(k);
- final String datasetId = dataset.getId();
- if (datasetId == null) {
- if (sVerbose) {
- Slog.v(TAG, "logContextCommitted() skipping idless dataset "
- + dataset);
- }
- } else {
- final ArrayList<AutofillValue> values = dataset.getFieldValues();
- for (int l = 0; l < values.size(); l++) {
- final AutofillValue candidate = values.get(l);
- if (currentValue.equals(candidate)) {
- if (sDebug) {
- Slog.d(TAG, "field " + viewState.id
- + " was manually filled with value set by "
- + "dataset " + datasetId);
- }
- if (manuallyFilledIds == null) {
- manuallyFilledIds = new ArrayMap<>();
- }
- ArraySet<String> datasetIds =
- manuallyFilledIds.get(viewState.id);
- if (datasetIds == null) {
- datasetIds = new ArraySet<>(1);
- manuallyFilledIds.put(viewState.id, datasetIds);
- }
- datasetIds.add(datasetId);
- }
- }
- if (mSelectedDatasetIds == null
- || !mSelectedDatasetIds.contains(datasetId)) {
- if (sVerbose) {
- Slog.v(TAG, "adding ignored dataset " + datasetId);
- }
- if (ignoredDatasets == null) {
- ignoredDatasets = new ArraySet<>();
- }
- ignoredDatasets.add(datasetId);
- }
+ // Check if value match a dataset.
+ if (hasAtLeastOneDataset) {
+ for (int j = 0; j < responseCount; j++) {
+ final FillResponse response = mResponses.valueAt(j);
+ final List<Dataset> datasets = response.getDatasets();
+ if (datasets == null || datasets.isEmpty()) {
+ if (sVerbose) {
+ Slog.v(TAG, "logContextCommitted() no datasets at " + j);
}
- }
- }
+ } else {
+ for (int k = 0; k < datasets.size(); k++) {
+ final Dataset dataset = datasets.get(k);
+ final String datasetId = dataset.getId();
+ if (datasetId == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "logContextCommitted() skipping idless "
+ + "dataset " + dataset);
+ }
+ } else {
+ final ArrayList<AutofillValue> values =
+ dataset.getFieldValues();
+ for (int l = 0; l < values.size(); l++) {
+ final AutofillValue candidate = values.get(l);
+ if (currentValue.equals(candidate)) {
+ if (sDebug) {
+ Slog.d(TAG, "field " + viewState.id + " was "
+ + "manually filled with value set by "
+ + "dataset " + datasetId);
+ }
+ if (manuallyFilledIds == null) {
+ manuallyFilledIds = new ArrayMap<>();
+ }
+ ArraySet<String> datasetIds =
+ manuallyFilledIds.get(viewState.id);
+ if (datasetIds == null) {
+ datasetIds = new ArraySet<>(1);
+ manuallyFilledIds.put(viewState.id, datasetIds);
+ }
+ datasetIds.add(datasetId);
+ }
+ } // for l
+ if (mSelectedDatasetIds == null
+ || !mSelectedDatasetIds.contains(datasetId)) {
+ if (sVerbose) {
+ Slog.v(TAG, "adding ignored dataset " + datasetId);
+ }
+ if (ignoredDatasets == null) {
+ ignoredDatasets = new ArraySet<>();
+ }
+ ignoredDatasets.add(datasetId);
+ } // if
+ } // if
+ } // for k
+ } // else
+ } // for j
}
- }
- }
+
+ // Check if detectable field changed.
+ if (detectableFieldId != null && detectableFieldId.equals(viewState.id)
+ && currentValue.isText() && currentValue.getTextValue() != null) {
+ final String actualValue = currentValue.getTextValue().toString();
+ final String expectedValue = fieldsDetection.getValue();
+ if (actualValue.equalsIgnoreCase(expectedValue)) {
+ detectedRemoteId = detectableRemoteId;
+ detectedFieldScore = 0;
+ } else if (sVerbose) {
+ Slog.v(TAG, "Detection mismatch for field " + detectableFieldId);
+ }
+ // TODO(b/67867469): set score on partial hits
+ }
+ } // else
+ } // else
}
if (sVerbose) {
@@ -1027,7 +1071,10 @@
+ ", ignoredDatasetIds=" + ignoredDatasets
+ ", changedAutofillIds=" + changedFieldIds
+ ", changedDatasetIds=" + changedDatasetIds
- + ", manuallyFilledIds=" + manuallyFilledIds);
+ + ", manuallyFilledIds=" + manuallyFilledIds
+ + ", detectableFieldId=" + detectableFieldId
+ + ", detectedFieldScore=" + detectedFieldScore
+ );
}
ArrayList<AutofillId> manuallyFilledFieldIds = null;
@@ -1045,9 +1092,11 @@
manuallyFilledDatasetIds.add(new ArrayList<>(datasetIds));
}
}
+
mService.logContextCommitted(id, mClientState, mSelectedDatasetIds, ignoredDatasets,
changedFieldIds, changedDatasetIds,
- manuallyFilledFieldIds, manuallyFilledDatasetIds);
+ manuallyFilledFieldIds, manuallyFilledDatasetIds,
+ detectedRemoteId, detectedFieldScore);
}
/**
@@ -1535,6 +1584,10 @@
viewState = new ViewState(this, id, this,
isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL);
mViewStates.put(id, viewState);
+
+ // TODO(b/67867469): for optimization purposes, should also ignore if change is
+ // detectable, and batch-send them when the session is finished (but that will
+ // require tracking detectable fields on AutofillManager)
if (isIgnored) {
if (sDebug) Slog.d(TAG, "updateLocked(): ignoring view " + id);
return;
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 1d8110f..832a66b 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -134,7 +134,11 @@
}
String getStateAsString() {
- return DebugUtils.flagsToString(ViewState.class, "STATE_", mState);
+ return getStateAsString(mState);
+ }
+
+ static String getStateAsString(int state) {
+ return DebugUtils.flagsToString(ViewState.class, "STATE_", state);
}
void setState(int state) {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 879faad..b2308d5 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -201,6 +201,8 @@
private static final String TAG_STATES = TAG + POSTFIX_STATES;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
+ // TODO(b/67864419): Remove once recents component is overridden
+ private static final String LEGACY_RECENTS_PACKAGE_NAME = "com.android.systemui.recents";
private static final boolean SHOW_ACTIVITY_START_TIME = true;
@@ -1057,7 +1059,8 @@
// We only allow home activities to be resizeable if they explicitly requested it.
info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
}
- } else if (service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) {
+ } else if (realActivity.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME) ||
+ service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) {
activityType = ACTIVITY_TYPE_RECENTS;
} else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT
&& canLaunchAssistActivity(launchedFromPackage)) {
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/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 781216c..cf9e6f3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -59,8 +59,6 @@
// vendor.
if ("pm.dexopt.inactive".equals(sysPropName)) {
sysPropValue = "verify";
- } else if ("pm.dexopt.shared".equals(sysPropName)) {
- sysPropValue = "speed";
} else {
sysPropValue = SystemProperties.get(sysPropName);
}
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index c86122f..011e1ac 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -112,6 +112,7 @@
UserManager.DISALLOW_OEM_UNLOCK,
UserManager.DISALLOW_UNMUTE_DEVICE,
UserManager.DISALLOW_AUTOFILL,
+ UserManager.DISALLOW_USER_SWITCH
});
/**
@@ -143,6 +144,13 @@
);
/**
+ * User restrictions that cannot be set by profile owners. Applied to all users.
+ */
+ private static final Set<String> DEVICE_OWNER_ONLY_RESTRICTIONS = Sets.newArraySet(
+ UserManager.DISALLOW_USER_SWITCH
+ );
+
+ /**
* User restrictions that can't be changed by device owner or profile owner.
*/
private static final Set<String> IMMUTABLE_BY_OWNERS = Sets.newArraySet(
@@ -311,6 +319,7 @@
*/
public static boolean canProfileOwnerChange(String restriction, int userId) {
return !IMMUTABLE_BY_OWNERS.contains(restriction)
+ && !DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction)
&& !(userId != UserHandle.USER_SYSTEM
&& PRIMARY_USER_ONLY_RESTRICTIONS.contains(restriction));
}
@@ -363,8 +372,9 @@
*/
private static boolean isGlobal(boolean isDeviceOwner, String key) {
return (isDeviceOwner &&
- (PRIMARY_USER_ONLY_RESTRICTIONS.contains(key)|| GLOBAL_RESTRICTIONS.contains(key)))
- || PROFILE_GLOBAL_RESTRICTIONS.contains(key);
+ (PRIMARY_USER_ONLY_RESTRICTIONS.contains(key) || GLOBAL_RESTRICTIONS.contains(key)))
+ || PROFILE_GLOBAL_RESTRICTIONS.contains(key)
+ || DEVICE_OWNER_ONLY_RESTRICTIONS.contains(key);
}
/**
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/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index 480be2e..882bf32 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -89,6 +89,7 @@
assertFalse(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_RECORD_AUDIO));
assertFalse(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_WALLPAPER));
assertTrue(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_ADD_USER));
+ assertTrue(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_USER_SWITCH));
}
public void testCanProfileOwnerChange() {
@@ -97,6 +98,8 @@
UserManager.DISALLOW_RECORD_AUDIO, user));
assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
UserManager.DISALLOW_WALLPAPER, user));
+ assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
+ UserManager.DISALLOW_USER_SWITCH, user));
assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
UserManager.DISALLOW_ADD_USER, user));
assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
@@ -109,6 +112,8 @@
UserManager.DISALLOW_WALLPAPER, user));
assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
UserManager.DISALLOW_ADD_USER, user));
+ assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
+ UserManager.DISALLOW_USER_SWITCH, user));
assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
UserManager.DISALLOW_ADJUST_VOLUME, user));
}
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/Telephony.java b/telephony/java/android/telephony/Telephony.java
index e40f20f..d7b6142 100644
--- a/telephony/java/android/telephony/Telephony.java
+++ b/telephony/java/android/telephony/Telephony.java
@@ -2828,6 +2828,26 @@
* @hide
*/
public static final int CARRIER_DELETED_BUT_PRESENT_IN_XML = 6;
+
+ /**
+ * The owner of the APN.
+ * <p>Type: INTEGER</p>
+ * @hide
+ */
+ public static final String OWNED_BY = "owned_by";
+
+ /**
+ * Possible value for the OWNED_BY field.
+ * APN is owned by DPC.
+ * @hide
+ */
+ public static final int OWNED_BY_DPC = 0;
+ /**
+ * Possible value for the OWNED_BY field.
+ * APN is owned by other sources.
+ * @hide
+ */
+ public static final int OWNED_BY_OTHERS = 1;
}
/**
diff --git a/telephony/java/android/telephony/ims/ImsServiceProxy.java b/telephony/java/android/telephony/ims/ImsServiceProxy.java
deleted file mode 100644
index 31d3db4..0000000
--- a/telephony/java/android/telephony/ims/ImsServiceProxy.java
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * 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.telephony.ims;
-
-import android.app.PendingIntent;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.telephony.ims.feature.IRcsFeature;
-import android.telephony.ims.feature.ImsFeature;
-import android.util.Log;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-import com.android.ims.internal.IImsConfig;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsServiceController;
-import com.android.ims.internal.IImsServiceFeatureListener;
-import com.android.ims.internal.IImsUt;
-
-/**
- * A container of the IImsServiceController binder, which implements all of the ImsFeatures that
- * the platform currently supports: MMTel and RCS.
- * @hide
- */
-
-public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeature {
-
- protected String LOG_TAG = "ImsServiceProxy";
- private final int mSupportedFeature;
-
- // Start by assuming the proxy is available for usage.
- private boolean mIsAvailable = true;
- // ImsFeature Status from the ImsService. Cached.
- private Integer mFeatureStatusCached = null;
- private ImsServiceProxy.INotifyStatusChanged mStatusCallback;
- private final Object mLock = new Object();
-
- public interface INotifyStatusChanged {
- void notifyStatusChanged();
- }
-
- private final IImsServiceFeatureListener mListenerBinder =
- new IImsServiceFeatureListener.Stub() {
-
- @Override
- public void imsFeatureCreated(int slotId, int feature) throws RemoteException {
- // The feature has been re-enabled. This may happen when the service crashes.
- synchronized (mLock) {
- if (!mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
- Log.i(LOG_TAG, "Feature enabled on slotId: " + slotId + " for feature: " +
- feature);
- mIsAvailable = true;
- }
- }
- }
-
- @Override
- public void imsFeatureRemoved(int slotId, int feature) throws RemoteException {
- synchronized (mLock) {
- if (mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
- Log.i(LOG_TAG, "Feature disabled on slotId: " + slotId + " for feature: " +
- feature);
- mIsAvailable = false;
- }
- }
- }
-
- @Override
- public void imsStatusChanged(int slotId, int feature, int status) throws RemoteException {
- synchronized (mLock) {
- Log.i(LOG_TAG, "imsStatusChanged: slot: " + slotId + " feature: " + feature +
- " status: " + status);
- if (mSlotId == slotId && feature == mSupportedFeature) {
- mFeatureStatusCached = status;
- if (mStatusCallback != null) {
- mStatusCallback.notifyStatusChanged();
- }
- }
- }
- }
- };
-
- public ImsServiceProxy(int slotId, IBinder binder, int featureType) {
- super(slotId, binder);
- mSupportedFeature = featureType;
- }
-
- public ImsServiceProxy(int slotId, int featureType) {
- super(slotId, null /*IBinder*/);
- mSupportedFeature = featureType;
- }
-
- public IImsServiceFeatureListener getListener() {
- return mListenerBinder;
- }
-
- public void setBinder(IBinder binder) {
- mBinder = binder;
- }
-
- @Override
- public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).startSession(mSlotId, mSupportedFeature,
- incomingCallIntent, listener);
- }
- }
-
- @Override
- public void endSession(int sessionId) throws RemoteException {
- synchronized (mLock) {
- // Only check to make sure the binder connection still exists. This method should
- // still be able to be called when the state is STATE_NOT_AVAILABLE.
- checkBinderConnection();
- getServiceInterface(mBinder).endSession(mSlotId, mSupportedFeature, sessionId);
- }
- }
-
- @Override
- public boolean isConnected(int callServiceType, int callType)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).isConnected(mSlotId, mSupportedFeature,
- callServiceType, callType);
- }
- }
-
- @Override
- public boolean isOpened() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).isOpened(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public void addRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- getServiceInterface(mBinder).addRegistrationListener(mSlotId, mSupportedFeature,
- listener);
- }
- }
-
- @Override
- public void removeRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- getServiceInterface(mBinder).removeRegistrationListener(mSlotId, mSupportedFeature,
- listener);
- }
- }
-
- @Override
- public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).createCallProfile(mSlotId, mSupportedFeature,
- sessionId, callServiceType, callType);
- }
- }
-
- @Override
- public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
- IImsCallSessionListener listener) throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).createCallSession(mSlotId, mSupportedFeature,
- sessionId, profile, listener);
- }
- }
-
- @Override
- public IImsCallSession getPendingCallSession(int sessionId, String callId)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).getPendingCallSession(mSlotId, mSupportedFeature,
- sessionId, callId);
- }
- }
-
- @Override
- public IImsUt getUtInterface() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).getUtInterface(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public IImsConfig getConfigInterface() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).getConfigInterface(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public void turnOnIms() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- getServiceInterface(mBinder).turnOnIms(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public void turnOffIms() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- getServiceInterface(mBinder).turnOffIms(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public IImsEcbm getEcbmInterface() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).getEcbmInterface(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public void setUiTTYMode(int uiTtyMode, Message onComplete)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- getServiceInterface(mBinder).setUiTTYMode(mSlotId, mSupportedFeature, uiTtyMode,
- onComplete);
- }
- }
-
- @Override
- public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).getMultiEndpointInterface(mSlotId,
- mSupportedFeature);
- }
- }
-
- @Override
- public int getFeatureStatus() {
- synchronized (mLock) {
- if (isBinderAlive() && mFeatureStatusCached != null) {
- Log.i(LOG_TAG, "getFeatureStatus - returning cached: " + mFeatureStatusCached);
- return mFeatureStatusCached;
- }
- }
- // Don't synchronize on Binder call.
- Integer status = retrieveFeatureStatus();
- synchronized (mLock) {
- if (status == null) {
- return ImsFeature.STATE_NOT_AVAILABLE;
- }
- // Cache only non-null value for feature status.
- mFeatureStatusCached = status;
- }
- Log.i(LOG_TAG, "getFeatureStatus - returning " + status);
- return status;
- }
-
- /**
- * Internal method used to retrieve the feature status from the corresponding ImsService.
- */
- private Integer retrieveFeatureStatus() {
- if (mBinder != null) {
- try {
- return getServiceInterface(mBinder).getFeatureStatus(mSlotId, mSupportedFeature);
- } catch (RemoteException e) {
- // Status check failed, don't update cache
- }
- }
- return null;
- }
-
- /**
- * @param c Callback that will fire when the feature status has changed.
- */
- public void setStatusCallback(INotifyStatusChanged c) {
- mStatusCallback = c;
- }
-
- @Override
- public boolean isBinderAlive() {
- return mIsAvailable && mBinder != null && mBinder.isBinderAlive();
- }
-
- protected void checkServiceIsReady() throws RemoteException {
- if (!isBinderReady()) {
- throw new RemoteException("ImsServiceProxy is not ready to accept commands.");
- }
- }
-
- private IImsServiceController getServiceInterface(IBinder b) {
- return IImsServiceController.Stub.asInterface(b);
- }
-}
diff --git a/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java b/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java
deleted file mode 100644
index 7ec9229..0000000
--- a/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * 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.telephony.ims;
-
-import android.app.PendingIntent;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.telephony.ims.feature.IMMTelFeature;
-import android.telephony.ims.feature.ImsFeature;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-import com.android.ims.internal.IImsConfig;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsService;
-import com.android.ims.internal.IImsUt;
-
-/**
- * Compatibility class that implements the new ImsService IMMTelFeature interface, but
- * uses the old IImsService interface to support older devices that implement the deprecated
- * opt/net/ims interface.
- * @hide
- */
-
-public class ImsServiceProxyCompat implements IMMTelFeature {
-
- private static final int SERVICE_ID = ImsFeature.MMTEL;
-
- protected final int mSlotId;
- protected IBinder mBinder;
-
- public ImsServiceProxyCompat(int slotId, IBinder binder) {
- mSlotId = slotId;
- mBinder = binder;
- }
-
- @Override
- public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
- throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).open(mSlotId, ImsFeature.MMTEL, incomingCallIntent,
- listener);
- }
-
- @Override
- public void endSession(int sessionId) throws RemoteException {
- checkBinderConnection();
- getServiceInterface(mBinder).close(sessionId);
- }
-
- @Override
- public boolean isConnected(int callServiceType, int callType)
- throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).isConnected(SERVICE_ID, callServiceType, callType);
- }
-
- @Override
- public boolean isOpened() throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).isOpened(SERVICE_ID);
- }
-
- @Override
- public void addRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException {
- checkBinderConnection();
- getServiceInterface(mBinder).addRegistrationListener(mSlotId, ImsFeature.MMTEL, listener);
- }
-
- @Override
- public void removeRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException {
- // Not Implemented in old ImsService. If the registration listener becomes invalid, the
- // ImsService will remove.
- }
-
- @Override
- public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
- throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).createCallProfile(sessionId, callServiceType, callType);
- }
-
- @Override
- public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
- IImsCallSessionListener listener) throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).createCallSession(sessionId, profile, listener);
- }
-
- @Override
- public IImsCallSession getPendingCallSession(int sessionId, String callId)
- throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).getPendingCallSession(sessionId, callId);
- }
-
- @Override
- public IImsUt getUtInterface() throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).getUtInterface(SERVICE_ID);
- }
-
- @Override
- public IImsConfig getConfigInterface() throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).getConfigInterface(mSlotId);
- }
-
- @Override
- public void turnOnIms() throws RemoteException {
- checkBinderConnection();
- getServiceInterface(mBinder).turnOnIms(mSlotId);
- }
-
- @Override
- public void turnOffIms() throws RemoteException {
- checkBinderConnection();
- getServiceInterface(mBinder).turnOffIms(mSlotId);
- }
-
- @Override
- public IImsEcbm getEcbmInterface() throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).getEcbmInterface(SERVICE_ID);
- }
-
- @Override
- public void setUiTTYMode(int uiTtyMode, Message onComplete)
- throws RemoteException {
- checkBinderConnection();
- getServiceInterface(mBinder).setUiTTYMode(SERVICE_ID, uiTtyMode, onComplete);
- }
-
- @Override
- public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).getMultiEndpointInterface(SERVICE_ID);
- }
-
- /**
- * Base implementation, always returns READY for compatibility with old ImsService.
- */
- public int getFeatureStatus() {
- return ImsFeature.STATE_READY;
- }
-
- /**
- * @return false if the binder connection is no longer alive.
- */
- public boolean isBinderAlive() {
- return mBinder != null && mBinder.isBinderAlive();
- }
-
- /**
- * @return Returns true if the ImsService is ready to take commands, false otherwise. If this
- * method returns false, it doesn't mean that the Binder connection is not available (use
- * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands
- * at this time.
- *
- * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take
- * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_NOT_AVAILABLE}.
- */
- public boolean isBinderReady() {
- return isBinderAlive() && getFeatureStatus() == ImsFeature.STATE_READY;
- }
-
- private IImsService getServiceInterface(IBinder b) {
- return IImsService.Stub.asInterface(b);
- }
-
- protected void checkBinderConnection() throws RemoteException {
- if (!isBinderAlive()) {
- throw new RemoteException("ImsServiceProxy is not available for that feature.");
- }
- }
-}
diff --git a/telephony/java/android/telephony/ims/feature/IMMTelFeature.java b/telephony/java/android/telephony/ims/feature/IMMTelFeature.java
deleted file mode 100644
index d65e27e..0000000
--- a/telephony/java/android/telephony/ims/feature/IMMTelFeature.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * 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.telephony.ims.feature;
-
-import android.app.PendingIntent;
-import android.os.Message;
-import android.os.RemoteException;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-import com.android.ims.internal.IImsConfig;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsUt;
-
-/**
- * MMTel interface for an ImsService. When updating this interface, ensure that base implementations
- * of your changes are also present in MMTelFeature for compatibility with older versions of the
- * MMTel feature.
- * @hide
- */
-
-public interface IMMTelFeature {
-
- /**
- * Notifies the MMTel feature that you would like to start a session. This should always be
- * done before making/receiving IMS calls. The IMS service will register the device to the
- * operator's network with the credentials (from ISIM) periodically in order to receive calls
- * from the operator's network. When the IMS service receives a new call, it will send out an
- * intent with the provided action string. The intent contains a call ID extra
- * {@link IImsCallSession#getCallId} and it can be used to take a call.
- *
- * @param incomingCallIntent When an incoming call is received, the IMS service will call
- * {@link PendingIntent#send} to send back the intent to the caller with
- * {@link #INCOMING_CALL_RESULT_CODE} as the result code and the intent to fill in the call ID;
- * It cannot be null.
- * @param listener To listen to IMS registration events; It cannot be null
- * @return an integer (greater than 0) representing the session id associated with the session
- * that has been started.
- */
- int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
- throws RemoteException;
-
- /**
- * End a previously started session using the associated sessionId.
- * @param sessionId an integer (greater than 0) representing the ongoing session. See
- * {@link #startSession}.
- */
- void endSession(int sessionId) throws RemoteException;
-
- /**
- * Checks if the IMS service has successfully registered to the IMS network with the specified
- * service & call type.
- *
- * @param callServiceType a service type that is specified in {@link ImsCallProfile}
- * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
- * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
- * @param callType a call type that is specified in {@link ImsCallProfile}
- * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
- * {@link ImsCallProfile#CALL_TYPE_VOICE}
- * {@link ImsCallProfile#CALL_TYPE_VT}
- * {@link ImsCallProfile#CALL_TYPE_VS}
- * @return true if the specified service id is connected to the IMS network; false otherwise
- * @throws RemoteException
- */
- boolean isConnected(int callServiceType, int callType) throws RemoteException;
-
- /**
- * Checks if the specified IMS service is opened.
- *
- * @return true if the specified service id is opened; false otherwise
- */
- boolean isOpened() throws RemoteException;
-
- /**
- * Add a new registration listener for the client associated with the session Id.
- * @param listener An implementation of IImsRegistrationListener.
- */
- void addRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException;
-
- /**
- * Remove a previously registered listener using {@link #addRegistrationListener} for the client
- * associated with the session Id.
- * @param listener A previously registered IImsRegistrationListener
- */
- void removeRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException;
-
- /**
- * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
- *
- * @param sessionId a session id which is obtained from {@link #startSession}
- * @param callServiceType a service type that is specified in {@link ImsCallProfile}
- * {@link ImsCallProfile#SERVICE_TYPE_NONE}
- * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
- * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
- * @param callType a call type that is specified in {@link ImsCallProfile}
- * {@link ImsCallProfile#CALL_TYPE_VOICE}
- * {@link ImsCallProfile#CALL_TYPE_VT}
- * {@link ImsCallProfile#CALL_TYPE_VT_TX}
- * {@link ImsCallProfile#CALL_TYPE_VT_RX}
- * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
- * {@link ImsCallProfile#CALL_TYPE_VS}
- * {@link ImsCallProfile#CALL_TYPE_VS_TX}
- * {@link ImsCallProfile#CALL_TYPE_VS_RX}
- * @return a {@link ImsCallProfile} object
- */
- ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
- throws RemoteException;
-
- /**
- * Creates a {@link ImsCallSession} with the specified call profile.
- * Use other methods, if applicable, instead of interacting with
- * {@link ImsCallSession} directly.
- *
- * @param sessionId a session id which is obtained from {@link #startSession}
- * @param profile a call profile to make the call
- * @param listener An implementation of IImsCallSessionListener.
- */
- IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
- IImsCallSessionListener listener) throws RemoteException;
-
- /**
- * Retrieves the call session associated with a pending call.
- *
- * @param sessionId a session id which is obtained from {@link #startSession}
- * @param callId a call id to make the call
- */
- IImsCallSession getPendingCallSession(int sessionId, String callId) throws RemoteException;
-
- /**
- * @return The Ut interface for the supplementary service configuration.
- */
- IImsUt getUtInterface() throws RemoteException;
-
- /**
- * @return The config interface for IMS Configuration
- */
- IImsConfig getConfigInterface() throws RemoteException;
-
- /**
- * Signal the MMTelFeature to turn on IMS when it has been turned off using {@link #turnOffIms}
- * @param sessionId a session id which is obtained from {@link #startSession}
- */
- void turnOnIms() throws RemoteException;
-
- /**
- * Signal the MMTelFeature to turn off IMS when it has been turned on using {@link #turnOnIms}
- * @param sessionId a session id which is obtained from {@link #startSession}
- */
- void turnOffIms() throws RemoteException;
-
- /**
- * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
- */
- IImsEcbm getEcbmInterface() throws RemoteException;
-
- /**
- * Sets the current UI TTY mode for the MMTelFeature.
- * @param uiTtyMode An integer containing the new UI TTY Mode.
- * @param onComplete A {@link Message} to be used when the mode has been set.
- * @throws RemoteException
- */
- void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException;
-
- /**
- * @return MultiEndpoint interface for DEP notifications
- */
- IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException;
-}
diff --git a/telephony/java/android/telephony/ims/feature/IRcsFeature.java b/telephony/java/android/telephony/ims/feature/IRcsFeature.java
deleted file mode 100644
index e28e1b3..0000000
--- a/telephony/java/android/telephony/ims/feature/IRcsFeature.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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.telephony.ims.feature;
-
-/**
- * Feature interface that provides access to RCS APIs. Currently empty until RCS support is added
- * in the framework.
- * @hide
- */
-
-public interface IRcsFeature {
-}
diff --git a/telephony/java/android/telephony/ims/feature/MMTelFeature.java b/telephony/java/android/telephony/ims/feature/MMTelFeature.java
index a71f0bf..758c379 100644
--- a/telephony/java/android/telephony/ims/feature/MMTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MMTelFeature.java
@@ -32,90 +32,183 @@
import java.util.List;
/**
- * Base implementation, which implements all methods in IMMTelFeature. Any class wishing to use
- * MMTelFeature should extend this class and implement all methods that the service supports.
+ * Base implementation for MMTel.
+ * Any class wishing to use MMTelFeature should extend this class and implement all methods that the
+ * service supports.
*
* @hide
*/
-public class MMTelFeature extends ImsFeature implements IMMTelFeature {
+public class MMTelFeature extends ImsFeature {
- @Override
+ /**
+ * Notifies the MMTel feature that you would like to start a session. This should always be
+ * done before making/receiving IMS calls. The IMS service will register the device to the
+ * operator's network with the credentials (from ISIM) periodically in order to receive calls
+ * from the operator's network. When the IMS service receives a new call, it will send out an
+ * intent with the provided action string. The intent contains a call ID extra
+ * {@link IImsCallSession#getCallId} and it can be used to take a call.
+ *
+ * @param incomingCallIntent When an incoming call is received, the IMS service will call
+ * {@link PendingIntent#send} to send back the intent to the caller with
+ * ImsManager#INCOMING_CALL_RESULT_CODE as the result code and the intent to fill in the call
+ * ID; It cannot be null.
+ * @param listener To listen to IMS registration events; It cannot be null
+ * @return an integer (greater than 0) representing the session id associated with the session
+ * that has been started.
+ */
public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener) {
return 0;
}
- @Override
+ /**
+ * End a previously started session using the associated sessionId.
+ * @param sessionId an integer (greater than 0) representing the ongoing session. See
+ * {@link #startSession}.
+ */
public void endSession(int sessionId) {
}
- @Override
+ /**
+ * Checks if the IMS service has successfully registered to the IMS network with the specified
+ * service & call type.
+ *
+ * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+ * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+ * @param callType a call type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE}
+ * {@link ImsCallProfile#CALL_TYPE_VT}
+ * {@link ImsCallProfile#CALL_TYPE_VS}
+ * @return true if the specified service id is connected to the IMS network; false otherwise
+ */
public boolean isConnected(int callSessionType, int callType) {
return false;
}
- @Override
+ /**
+ * Checks if the specified IMS service is opened.
+ *
+ * @return true if the specified service id is opened; false otherwise
+ */
public boolean isOpened() {
return false;
}
- @Override
+ /**
+ * Add a new registration listener for the client associated with the session Id.
+ * @param listener An implementation of IImsRegistrationListener.
+ */
public void addRegistrationListener(IImsRegistrationListener listener) {
}
- @Override
+ /**
+ * Remove a previously registered listener using {@link #addRegistrationListener} for the client
+ * associated with the session Id.
+ * @param listener A previously registered IImsRegistrationListener
+ */
public void removeRegistrationListener(IImsRegistrationListener listener) {
}
- @Override
+ /**
+ * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#SERVICE_TYPE_NONE}
+ * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+ * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+ * @param callType a call type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE}
+ * {@link ImsCallProfile#CALL_TYPE_VT}
+ * {@link ImsCallProfile#CALL_TYPE_VT_TX}
+ * {@link ImsCallProfile#CALL_TYPE_VT_RX}
+ * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
+ * {@link ImsCallProfile#CALL_TYPE_VS}
+ * {@link ImsCallProfile#CALL_TYPE_VS_TX}
+ * {@link ImsCallProfile#CALL_TYPE_VS_RX}
+ * @return a {@link ImsCallProfile} object
+ */
public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType) {
return null;
}
- @Override
+ /**
+ * Creates a {@link ImsCallSession} with the specified call profile.
+ * Use other methods, if applicable, instead of interacting with
+ * {@link ImsCallSession} directly.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param profile a call profile to make the call
+ * @param listener An implementation of IImsCallSessionListener.
+ */
public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
IImsCallSessionListener listener) {
return null;
}
- @Override
+ /**
+ * Retrieves the call session associated with a pending call.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param callId a call id to make the call
+ */
public IImsCallSession getPendingCallSession(int sessionId, String callId) {
return null;
}
- @Override
+ /**
+ * @return The Ut interface for the supplementary service configuration.
+ */
public IImsUt getUtInterface() {
return null;
}
- @Override
+ /**
+ * @return The config interface for IMS Configuration
+ */
public IImsConfig getConfigInterface() {
return null;
}
- @Override
+ /**
+ * Signal the MMTelFeature to turn on IMS when it has been turned off using {@link #turnOffIms}
+ */
public void turnOnIms() {
}
- @Override
+ /**
+ * Signal the MMTelFeature to turn off IMS when it has been turned on using {@link #turnOnIms}
+ */
public void turnOffIms() {
}
- @Override
+ /**
+ * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
+ */
public IImsEcbm getEcbmInterface() {
return null;
}
- @Override
+ /**
+ * Sets the current UI TTY mode for the MMTelFeature.
+ * @param uiTtyMode An integer containing the new UI TTY Mode.
+ * @param onComplete A {@link Message} to be used when the mode has been set.
+ */
public void setUiTTYMode(int uiTtyMode, Message onComplete) {
}
- @Override
+ /**
+ * @return MultiEndpoint interface for DEP notifications
+ */
public IImsMultiEndpoint getMultiEndpointInterface() {
return null;
}
- @Override
+ /**
+ * {@inheritDoc}
+ */
public void onFeatureRemoved() {
}
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 9cddc1b..332cca3 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -18,11 +18,11 @@
/**
* Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
- * this class and provide implementations of the IRcsFeature methods that they support.
+ * this class and provide implementations of the RcsFeature methods that they support.
* @hide
*/
-public class RcsFeature extends ImsFeature implements IRcsFeature {
+public class RcsFeature extends ImsFeature {
public RcsFeature() {
super();
diff --git a/telephony/java/com/android/ims/internal/IImsServiceController.aidl b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
index bb06d7e..f1e2262 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceController.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
@@ -31,7 +31,7 @@
import android.os.Message;
/**
- * See ImsService and IMMTelFeature for more information.
+ * See ImsService and MMTelFeature for more information.
* {@hide}
*/
interface IImsServiceController {
diff --git a/tests/UiBench/Android.mk b/tests/UiBench/Android.mk
index 0824c26..60327e5 100644
--- a/tests/UiBench/Android.mk
+++ b/tests/UiBench/Android.mk
@@ -10,25 +10,11 @@
# use appcompat/support lib from the tree, so improvements/
# regressions are reflected in test data
-LOCAL_RESOURCE_DIR := \
- $(LOCAL_PATH)/res \
- frameworks/support/core-ui/res \
- frameworks/support/design/res \
- frameworks/support/v7/appcompat/res \
- frameworks/support/v7/cardview/res \
- frameworks/support/v7/recyclerview/res \
- frameworks/support/v17/leanback/res
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_AAPT_FLAGS := \
- --auto-add-overlay \
- --extra-packages android.support.coreui \
- --extra-packages android.support.design \
- --extra-packages android.support.v7.appcompat \
- --extra-packages android.support.v7.cardview \
- --extra-packages android.support.v7.recyclerview \
- --extra-packages android.support.v17.leanback
+LOCAL_USE_AAPT2 := true
-LOCAL_STATIC_JAVA_LIBRARIES := \
+LOCAL_STATIC_ANDROID_LIBRARIES := \
android-support-design \
android-support-v4 \
android-support-v7-appcompat \
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 9e97d84b..5c031eb 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -64,6 +64,13 @@
return Arrays.asList(new Object[][] {{"8.8.4.4"}, {"2601::10"}});
}
+ private static final byte[] AEAD_KEY = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x73, 0x61, 0x6C, 0x74
+ };
private static final byte[] CRYPT_KEY = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
@@ -87,7 +94,7 @@
private static final IpSecAlgorithm CRYPT_ALGO =
new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
private static final IpSecAlgorithm AEAD_ALGO =
- new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, CRYPT_KEY, CRYPT_KEY.length * 4);
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
private static final int[] DIRECTIONS =
new int[] {IpSecTransform.DIRECTION_IN, IpSecTransform.DIRECTION_OUT};
@@ -262,7 +269,7 @@
eq(new byte[] {}),
eq(0),
eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
- eq(CRYPT_KEY),
+ eq(AEAD_KEY),
anyInt(),
anyInt(),
anyInt(),
@@ -283,7 +290,7 @@
eq(new byte[] {}),
eq(0),
eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
- eq(CRYPT_KEY),
+ eq(AEAD_KEY),
anyInt(),
anyInt(),
anyInt(),
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 6fb1793..de4fb73 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -127,9 +127,9 @@
diag->Error(DiagMessage(el->line_number)
<< "attribute 'package' in <manifest> tag must not be a reference");
return false;
- } else if (!util::IsJavaPackageName(attr->value)) {
+ } else if (!util::IsAndroidPackageName(attr->value)) {
diag->Error(DiagMessage(el->line_number)
- << "attribute 'package' in <manifest> tag is not a valid Java package name: '"
+ << "attribute 'package' in <manifest> tag is not a valid Android package name: '"
<< attr->value << "'");
return false;
}
diff --git a/tools/aapt2/text/Unicode.cpp b/tools/aapt2/text/Unicode.cpp
index 75eeb46..3735b3e 100644
--- a/tools/aapt2/text/Unicode.cpp
+++ b/tools/aapt2/text/Unicode.cpp
@@ -85,7 +85,8 @@
return false;
}
- if (!IsXidStart(iter.Next())) {
+ const char32_t first_codepoint = iter.Next();
+ if (!IsXidStart(first_codepoint) && first_codepoint != U'_' && first_codepoint != U'$') {
return false;
}
diff --git a/tools/aapt2/text/Unicode_test.cpp b/tools/aapt2/text/Unicode_test.cpp
index d47fb28..a8e797c 100644
--- a/tools/aapt2/text/Unicode_test.cpp
+++ b/tools/aapt2/text/Unicode_test.cpp
@@ -44,10 +44,11 @@
TEST(UnicodeTest, IsJavaIdentifier) {
EXPECT_TRUE(IsJavaIdentifier("FøøBar_12"));
EXPECT_TRUE(IsJavaIdentifier("Føø$Bar"));
+ EXPECT_TRUE(IsJavaIdentifier("_FøøBar"));
+ EXPECT_TRUE(IsJavaIdentifier("$Føø$Bar"));
EXPECT_FALSE(IsJavaIdentifier("12FøøBar"));
- EXPECT_FALSE(IsJavaIdentifier("_FøøBar"));
- EXPECT_FALSE(IsJavaIdentifier("$Føø$Bar"));
+ EXPECT_FALSE(IsJavaIdentifier(".Hello"));
}
TEST(UnicodeTest, IsValidResourceEntryName) {
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index a9b49d9..e42145d 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -24,6 +24,7 @@
#include "androidfw/StringPiece.h"
#include "utils/Unicode.h"
+#include "text/Unicode.h"
#include "text/Utf8Iterator.h"
#include "util/BigBuffer.h"
#include "util/Maybe.h"
@@ -94,72 +95,55 @@
return StringPiece(start, end - start);
}
-StringPiece::const_iterator FindNonAlphaNumericAndNotInSet(
- const StringPiece& str, const StringPiece& allowed_chars) {
- const auto end_iter = str.end();
- for (auto iter = str.begin(); iter != end_iter; ++iter) {
- char c = *iter;
- if ((c >= u'a' && c <= u'z') || (c >= u'A' && c <= u'Z') ||
- (c >= u'0' && c <= u'9')) {
- continue;
- }
-
- bool match = false;
- for (char i : allowed_chars) {
- if (c == i) {
- match = true;
- break;
- }
- }
-
- if (!match) {
- return iter;
+static int IsJavaNameImpl(const StringPiece& str) {
+ int pieces = 0;
+ for (const StringPiece& piece : Tokenize(str, '.')) {
+ pieces++;
+ if (!text::IsJavaIdentifier(piece)) {
+ return -1;
}
}
- return end_iter;
+ return pieces;
}
bool IsJavaClassName(const StringPiece& str) {
- size_t pieces = 0;
- for (const StringPiece& piece : Tokenize(str, '.')) {
- pieces++;
- if (piece.empty()) {
- return false;
- }
-
- // Can't have starting or trailing $ character.
- if (piece.data()[0] == '$' || piece.data()[piece.size() - 1] == '$') {
- return false;
- }
-
- if (FindNonAlphaNumericAndNotInSet(piece, "$_") != piece.end()) {
- return false;
- }
- }
- return pieces >= 2;
+ return IsJavaNameImpl(str) >= 2;
}
bool IsJavaPackageName(const StringPiece& str) {
- if (str.empty()) {
- return false;
- }
+ return IsJavaNameImpl(str) >= 1;
+}
- size_t pieces = 0;
+static int IsAndroidNameImpl(const StringPiece& str) {
+ int pieces = 0;
for (const StringPiece& piece : Tokenize(str, '.')) {
- pieces++;
if (piece.empty()) {
- return false;
+ return -1;
}
- if (piece.data()[0] == '_' || piece.data()[piece.size() - 1] == '_') {
- return false;
+ const char first_character = piece.data()[0];
+ if (!::isalpha(first_character)) {
+ return -1;
}
- if (FindNonAlphaNumericAndNotInSet(piece, "_") != piece.end()) {
- return false;
+ bool valid = std::all_of(piece.begin() + 1, piece.end(), [](const char c) -> bool {
+ return ::isalnum(c) || c == '_';
+ });
+
+ if (!valid) {
+ return -1;
}
+ pieces++;
}
- return pieces >= 1;
+ return pieces;
+}
+
+bool IsAndroidPackageName(const StringPiece& str) {
+ return IsAndroidNameImpl(str) > 1 || str == "android";
+}
+
+bool IsAndroidSplitName(const StringPiece& str) {
+ return IsAndroidNameImpl(str) > 0;
}
Maybe<std::string> GetFullyQualifiedClassName(const StringPiece& package,
@@ -176,7 +160,7 @@
return {};
}
- std::string result(package.data(), package.size());
+ std::string result = package.to_string();
if (classname.data()[0] != '.') {
result += '.';
}
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index c928458..7c949b90 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -53,48 +53,40 @@
std::vector<std::string> Split(const android::StringPiece& str, char sep);
std::vector<std::string> SplitAndLowercase(const android::StringPiece& str, char sep);
-/**
- * Returns true if the string starts with prefix.
- */
+// Returns true if the string starts with prefix.
bool StartsWith(const android::StringPiece& str, const android::StringPiece& prefix);
-/**
- * Returns true if the string ends with suffix.
- */
+// Returns true if the string ends with suffix.
bool EndsWith(const android::StringPiece& str, const android::StringPiece& suffix);
-/**
- * Creates a new StringPiece16 that points to a substring
- * of the original string without leading or trailing whitespace.
- */
+// Creates a new StringPiece16 that points to a substring of the original string without leading or
+// trailing whitespace.
android::StringPiece TrimWhitespace(const android::StringPiece& str);
-/**
- * Returns an iterator to the first character that is not alpha-numeric and that
- * is not in the allowedChars set.
- */
-android::StringPiece::const_iterator FindNonAlphaNumericAndNotInSet(
- const android::StringPiece& str, const android::StringPiece& allowed_chars);
-
-/**
- * Tests that the string is a valid Java class name.
- */
+// Tests that the string is a valid Java class name.
bool IsJavaClassName(const android::StringPiece& str);
-/**
- * Tests that the string is a valid Java package name.
- */
+// Tests that the string is a valid Java package name.
bool IsJavaPackageName(const android::StringPiece& str);
-/**
- * Converts the class name to a fully qualified class name from the given
- * `package`. Ex:
- *
- * asdf --> package.asdf
- * .asdf --> package.asdf
- * .a.b --> package.a.b
- * asdf.adsf --> asdf.adsf
- */
+// Tests that the string is a valid Android package name. More strict than a Java package name.
+// - First character of each component (separated by '.') must be an ASCII letter.
+// - Subsequent characters of a component can be ASCII alphanumeric or an underscore.
+// - Package must contain at least two components, unless it is 'android'.
+bool IsAndroidPackageName(const android::StringPiece& str);
+
+// Tests that the string is a valid Android split name.
+// - First character of each component (separated by '.') must be an ASCII letter.
+// - Subsequent characters of a component can be ASCII alphanumeric or an underscore.
+bool IsAndroidSplitName(const android::StringPiece& str);
+
+// Converts the class name to a fully qualified class name from the given
+// `package`. Ex:
+//
+// asdf --> package.asdf
+// .asdf --> package.asdf
+// .a.b --> package.a.b
+// asdf.adsf --> asdf.adsf
Maybe<std::string> GetFullyQualifiedClassName(const android::StringPiece& package,
const android::StringPiece& class_name);
@@ -108,23 +100,17 @@
return 0;
}
-/**
- * Makes a std::unique_ptr<> with the template parameter inferred by the compiler.
- * This will be present in C++14 and can be removed then.
- */
+// Makes a std::unique_ptr<> with the template parameter inferred by the compiler.
+// This will be present in C++14 and can be removed then.
template <typename T, class... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}
-/**
- * Writes a set of items to the std::ostream, joining the times with the
- * provided
- * separator.
- */
+// Writes a set of items to the std::ostream, joining the times with the provided separator.
template <typename Container>
-::std::function<::std::ostream&(::std::ostream&)> Joiner(
- const Container& container, const char* sep) {
+::std::function<::std::ostream&(::std::ostream&)> Joiner(const Container& container,
+ const char* sep) {
using std::begin;
using std::end;
const auto begin_iter = begin(container);
@@ -140,32 +126,19 @@
};
}
-/**
- * Helper method to extract a UTF-16 string from a StringPool. If the string is
- * stored as UTF-8,
- * the conversion to UTF-16 happens within ResStringPool.
- */
+// Helper method to extract a UTF-16 string from a StringPool. If the string is stored as UTF-8,
+// the conversion to UTF-16 happens within ResStringPool.
android::StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx);
-/**
- * Helper method to extract a UTF-8 string from a StringPool. If the string is
- * stored as UTF-16,
- * the conversion from UTF-16 to UTF-8 does not happen in ResStringPool and is
- * done by this method,
- * which maintains no state or cache. This means we must return an std::string
- * copy.
- */
+// Helper method to extract a UTF-8 string from a StringPool. If the string is stored as UTF-16,
+// the conversion from UTF-16 to UTF-8 does not happen in ResStringPool and is done by this method,
+// which maintains no state or cache. This means we must return an std::string copy.
std::string GetString(const android::ResStringPool& pool, size_t idx);
-/**
- * Checks that the Java string format contains no non-positional arguments
- * (arguments without
- * explicitly specifying an index) when there are more than one argument. This
- * is an error
- * because translations may rearrange the order of the arguments in the string,
- * which will
- * break the string interpolation.
- */
+// Checks that the Java string format contains no non-positional arguments (arguments without
+// explicitly specifying an index) when there are more than one argument. This is an error
+// because translations may rearrange the order of the arguments in the string, which will
+// break the string interpolation.
bool VerifyJavaStringFormat(const android::StringPiece& str);
class StringBuilder {
@@ -194,36 +167,38 @@
std::string error_;
};
-inline const std::string& StringBuilder::ToString() const { return str_; }
+inline const std::string& StringBuilder::ToString() const {
+ return str_;
+}
-inline const std::string& StringBuilder::Error() const { return error_; }
+inline const std::string& StringBuilder::Error() const {
+ return error_;
+}
-inline bool StringBuilder::IsEmpty() const { return str_.empty(); }
+inline bool StringBuilder::IsEmpty() const {
+ return str_.empty();
+}
-inline size_t StringBuilder::Utf16Len() const { return utf16_len_; }
+inline size_t StringBuilder::Utf16Len() const {
+ return utf16_len_;
+}
-inline StringBuilder::operator bool() const { return error_.empty(); }
+inline StringBuilder::operator bool() const {
+ return error_.empty();
+}
-/**
- * Converts a UTF8 string to a UTF16 string.
- */
+// Converts a UTF8 string to a UTF16 string.
std::u16string Utf8ToUtf16(const android::StringPiece& utf8);
std::string Utf16ToUtf8(const android::StringPiece16& utf16);
-/**
- * Writes the entire BigBuffer to the output stream.
- */
+// Writes the entire BigBuffer to the output stream.
bool WriteAll(std::ostream& out, const BigBuffer& buffer);
-/*
- * Copies the entire BigBuffer into a single buffer.
- */
+// Copies the entire BigBuffer into a single buffer.
std::unique_ptr<uint8_t[]> Copy(const BigBuffer& buffer);
-/**
- * A Tokenizer implemented as an iterable collection. It does not allocate
- * any memory on the heap nor use standard containers.
- */
+// A Tokenizer implemented as an iterable collection. It does not allocate any memory on the heap
+// nor use standard containers.
class Tokenizer {
public:
class iterator {
@@ -269,38 +244,42 @@
const iterator end_;
};
-inline Tokenizer Tokenize(const android::StringPiece& str, char sep) { return Tokenizer(str, sep); }
+inline Tokenizer Tokenize(const android::StringPiece& str, char sep) {
+ return Tokenizer(str, sep);
+}
-inline uint16_t HostToDevice16(uint16_t value) { return htods(value); }
+inline uint16_t HostToDevice16(uint16_t value) {
+ return htods(value);
+}
-inline uint32_t HostToDevice32(uint32_t value) { return htodl(value); }
+inline uint32_t HostToDevice32(uint32_t value) {
+ return htodl(value);
+}
-inline uint16_t DeviceToHost16(uint16_t value) { return dtohs(value); }
+inline uint16_t DeviceToHost16(uint16_t value) {
+ return dtohs(value);
+}
-inline uint32_t DeviceToHost32(uint32_t value) { return dtohl(value); }
+inline uint32_t DeviceToHost32(uint32_t value) {
+ return dtohl(value);
+}
-/**
- * Given a path like: res/xml-sw600dp/foo.xml
- *
- * Extracts "res/xml-sw600dp/" into outPrefix.
- * Extracts "foo" into outEntry.
- * Extracts ".xml" into outSuffix.
- *
- * Returns true if successful.
- */
+// Given a path like: res/xml-sw600dp/foo.xml
+//
+// Extracts "res/xml-sw600dp/" into outPrefix.
+// Extracts "foo" into outEntry.
+// Extracts ".xml" into outSuffix.
+//
+// Returns true if successful.
bool ExtractResFilePathParts(const android::StringPiece& path, android::StringPiece* out_prefix,
android::StringPiece* out_entry, android::StringPiece* out_suffix);
} // namespace util
-/**
- * Stream operator for functions. Calls the function with the stream as an
- * argument.
- * In the aapt namespace for lookup.
- */
-inline ::std::ostream& operator<<(
- ::std::ostream& out,
- const ::std::function<::std::ostream&(::std::ostream&)>& f) {
+// Stream operator for functions. Calls the function with the stream as an argument.
+// In the aapt namespace for lookup.
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const ::std::function<::std::ostream&(::std::ostream&)>& f) {
return f(out);
}
diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp
index adb5291..2d1242a 100644
--- a/tools/aapt2/util/Util_test.cpp
+++ b/tools/aapt2/util/Util_test.cpp
@@ -117,24 +117,46 @@
EXPECT_TRUE(util::IsJavaClassName("android.test.Class$Inner"));
EXPECT_TRUE(util::IsJavaClassName("android_test.test.Class"));
EXPECT_TRUE(util::IsJavaClassName("_android_.test._Class_"));
- EXPECT_FALSE(util::IsJavaClassName("android.test.$Inner"));
- EXPECT_FALSE(util::IsJavaClassName("android.test.Inner$"));
+ EXPECT_TRUE(util::IsJavaClassName("android.test.$Inner"));
+ EXPECT_TRUE(util::IsJavaClassName("android.test.Inner$"));
+ EXPECT_TRUE(util::IsJavaClassName("com.foo.FøøBar"));
+
EXPECT_FALSE(util::IsJavaClassName(".test.Class"));
EXPECT_FALSE(util::IsJavaClassName("android"));
+ EXPECT_FALSE(util::IsJavaClassName("FooBar"));
}
TEST(UtilTest, IsJavaPackageName) {
EXPECT_TRUE(util::IsJavaPackageName("android"));
EXPECT_TRUE(util::IsJavaPackageName("android.test"));
EXPECT_TRUE(util::IsJavaPackageName("android.test_thing"));
- EXPECT_FALSE(util::IsJavaPackageName("_android"));
- EXPECT_FALSE(util::IsJavaPackageName("android_"));
+ EXPECT_TRUE(util::IsJavaPackageName("_android"));
+ EXPECT_TRUE(util::IsJavaPackageName("android_"));
+ EXPECT_TRUE(util::IsJavaPackageName("android._test"));
+ EXPECT_TRUE(util::IsJavaPackageName("cøm.foo"));
+
EXPECT_FALSE(util::IsJavaPackageName("android."));
EXPECT_FALSE(util::IsJavaPackageName(".android"));
- EXPECT_FALSE(util::IsJavaPackageName("android._test"));
EXPECT_FALSE(util::IsJavaPackageName(".."));
}
+TEST(UtilTest, IsAndroidPackageName) {
+ EXPECT_TRUE(util::IsAndroidPackageName("android"));
+ EXPECT_TRUE(util::IsAndroidPackageName("android.test"));
+ EXPECT_TRUE(util::IsAndroidPackageName("com.foo"));
+ EXPECT_TRUE(util::IsAndroidPackageName("com.foo.test_thing"));
+ EXPECT_TRUE(util::IsAndroidPackageName("com.foo.testing_thing_"));
+ EXPECT_TRUE(util::IsAndroidPackageName("com.foo.test_99_"));
+
+ EXPECT_FALSE(util::IsAndroidPackageName("android._test"));
+ EXPECT_FALSE(util::IsAndroidPackageName("com"));
+ EXPECT_FALSE(util::IsAndroidPackageName("_android"));
+ EXPECT_FALSE(util::IsAndroidPackageName("android."));
+ EXPECT_FALSE(util::IsAndroidPackageName(".android"));
+ EXPECT_FALSE(util::IsAndroidPackageName(".."));
+ EXPECT_FALSE(util::IsAndroidPackageName("cøm.foo"));
+}
+
TEST(UtilTest, FullyQualifiedClassName) {
EXPECT_THAT(util::GetFullyQualifiedClassName("android", ".asdf"), Eq("android.asdf"));
EXPECT_THAT(util::GetFullyQualifiedClassName("android", ".a.b"), Eq("android.a.b"));
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index a367b23..bf8fed1 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -348,6 +348,9 @@
* quotation marks. Otherwise, it is returned as a string of hex digits. The
* SSID may be <unknown ssid> if there is no network currently connected,
* or if the caller has insufficient permissions to access the SSID.
+ *
+ * Prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method
+ * always returned the SSID with no quotes around it.
* @return the SSID
*/
public String getSSID() {