Merge "API for notification listener for Companioon apps" into oc-dev
diff --git a/api/current.txt b/api/current.txt
index 7f0d5b9..c1d6349 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8078,7 +8078,12 @@
method public void flushPendingScanResults(android.bluetooth.le.ScanCallback);
method public void startScan(android.bluetooth.le.ScanCallback);
method public void startScan(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
+ method public int startScan(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.app.PendingIntent);
method public void stopScan(android.bluetooth.le.ScanCallback);
+ method public void stopScan(android.app.PendingIntent);
+ field public static final java.lang.String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE";
+ field public static final java.lang.String EXTRA_ERROR_CODE = "android.bluetooth.le.extra.ERROR_CODE";
+ field public static final java.lang.String EXTRA_LIST_SCAN_RESULT = "android.bluetooth.le.extra.LIST_SCAN_RESULT";
}
public final class PeriodicAdvertisingParameters implements android.os.Parcelable {
@@ -25792,6 +25797,10 @@
method public android.net.NetworkRequest.Builder removeCapability(int);
method public android.net.NetworkRequest.Builder removeTransportType(int);
method public android.net.NetworkRequest.Builder setNetworkSpecifier(java.lang.String);
+ method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
+ }
+
+ public abstract class NetworkSpecifier {
}
public class ParseException extends java.lang.RuntimeException {
@@ -37045,8 +37054,10 @@
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConnected();
method public void onDisconnected();
- method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
- method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
+ method public void onFillRequest(android.service.autofill.FillRequest, android.os.CancellationSignal, android.service.autofill.FillCallback);
+ method public abstract deprecated void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
+ method public void onSaveRequest(android.service.autofill.SaveRequest, android.service.autofill.SaveCallback);
+ method public abstract deprecated void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillService";
field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
}
@@ -37071,6 +37082,25 @@
method public void onSuccess(android.service.autofill.FillResponse);
}
+ public final class FillContext implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getRequestId();
+ method public android.app.assist.AssistStructure getStructure();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillContext> CREATOR;
+ }
+
+ public final class FillRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getClientState();
+ method public int getFlags();
+ method public int getId();
+ method public android.app.assist.AssistStructure getStructure();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillRequest> CREATOR;
+ field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
+ }
+
public final class FillResponse implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -37082,7 +37112,8 @@
method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset);
method public android.service.autofill.FillResponse build();
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 setExtras(android.os.Bundle);
+ method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle);
+ method public deprecated android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
}
@@ -37111,6 +37142,14 @@
method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
}
+ public final class SaveRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getClientState();
+ method public java.util.List<android.service.autofill.FillContext> getFillContexts();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
+ }
+
}
package android.service.carrier {
@@ -47806,7 +47845,7 @@
field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
field public static final java.lang.String EXTRA_DATA_EXTRAS = "android.view.autofill.extra.DATA_EXTRAS";
- field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
+ field public static final deprecated int FLAG_MANUAL_REQUEST = 1; // 0x1
}
public static abstract class AutofillManager.AutofillCallback {
diff --git a/api/system-current.txt b/api/system-current.txt
index c0f21f2..ec089c0 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8550,10 +8550,15 @@
method public void flushPendingScanResults(android.bluetooth.le.ScanCallback);
method public void startScan(android.bluetooth.le.ScanCallback);
method public void startScan(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
+ method public int startScan(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.app.PendingIntent);
method public void startScanFromSource(android.os.WorkSource, android.bluetooth.le.ScanCallback);
method public void startScanFromSource(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.os.WorkSource, android.bluetooth.le.ScanCallback);
method public void startTruncatedScan(java.util.List<android.bluetooth.le.TruncatedFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
method public void stopScan(android.bluetooth.le.ScanCallback);
+ method public void stopScan(android.app.PendingIntent);
+ field public static final java.lang.String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE";
+ field public static final java.lang.String EXTRA_ERROR_CODE = "android.bluetooth.le.extra.ERROR_CODE";
+ field public static final java.lang.String EXTRA_LIST_SCAN_RESULT = "android.bluetooth.le.extra.LIST_SCAN_RESULT";
}
public final class PeriodicAdvertisingParameters implements android.os.Parcelable {
@@ -28015,6 +28020,7 @@
method public android.net.NetworkRequest.Builder removeCapability(int);
method public android.net.NetworkRequest.Builder removeTransportType(int);
method public android.net.NetworkRequest.Builder setNetworkSpecifier(java.lang.String);
+ method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
}
public class NetworkScoreManager {
@@ -28033,6 +28039,9 @@
field public static final java.lang.String EXTRA_PACKAGE_NAME = "packageName";
}
+ public abstract class NetworkSpecifier {
+ }
+
public class ParseException extends java.lang.RuntimeException {
field public java.lang.String response;
}
@@ -40162,8 +40171,10 @@
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConnected();
method public void onDisconnected();
- method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
- method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
+ method public void onFillRequest(android.service.autofill.FillRequest, android.os.CancellationSignal, android.service.autofill.FillCallback);
+ method public abstract deprecated void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
+ method public void onSaveRequest(android.service.autofill.SaveRequest, android.service.autofill.SaveCallback);
+ method public abstract deprecated void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillService";
field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
}
@@ -40188,6 +40199,25 @@
method public void onSuccess(android.service.autofill.FillResponse);
}
+ public final class FillContext implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getRequestId();
+ method public android.app.assist.AssistStructure getStructure();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillContext> CREATOR;
+ }
+
+ public final class FillRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getClientState();
+ method public int getFlags();
+ method public int getId();
+ method public android.app.assist.AssistStructure getStructure();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillRequest> CREATOR;
+ field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
+ }
+
public final class FillResponse implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -40199,7 +40229,8 @@
method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset);
method public android.service.autofill.FillResponse build();
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 setExtras(android.os.Bundle);
+ method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle);
+ method public deprecated android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
}
@@ -40228,6 +40259,14 @@
method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
}
+ public final class SaveRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getClientState();
+ method public java.util.List<android.service.autofill.FillContext> getFillContexts();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
+ }
+
}
package android.service.carrier {
@@ -51390,7 +51429,7 @@
field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
field public static final java.lang.String EXTRA_DATA_EXTRAS = "android.view.autofill.extra.DATA_EXTRAS";
- field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
+ field public static final deprecated int FLAG_MANUAL_REQUEST = 1; // 0x1
}
public static abstract class AutofillManager.AutofillCallback {
diff --git a/api/test-current.txt b/api/test-current.txt
index 04354e3..ba50949 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -8109,7 +8109,12 @@
method public void flushPendingScanResults(android.bluetooth.le.ScanCallback);
method public void startScan(android.bluetooth.le.ScanCallback);
method public void startScan(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
+ method public int startScan(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.app.PendingIntent);
method public void stopScan(android.bluetooth.le.ScanCallback);
+ method public void stopScan(android.app.PendingIntent);
+ field public static final java.lang.String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE";
+ field public static final java.lang.String EXTRA_ERROR_CODE = "android.bluetooth.le.extra.ERROR_CODE";
+ field public static final java.lang.String EXTRA_LIST_SCAN_RESULT = "android.bluetooth.le.extra.LIST_SCAN_RESULT";
}
public final class PeriodicAdvertisingParameters implements android.os.Parcelable {
@@ -25899,6 +25904,10 @@
method public android.net.NetworkRequest.Builder removeCapability(int);
method public android.net.NetworkRequest.Builder removeTransportType(int);
method public android.net.NetworkRequest.Builder setNetworkSpecifier(java.lang.String);
+ method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
+ }
+
+ public abstract class NetworkSpecifier {
}
public class ParseException extends java.lang.RuntimeException {
@@ -37198,8 +37207,10 @@
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConnected();
method public void onDisconnected();
- method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
- method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
+ method public void onFillRequest(android.service.autofill.FillRequest, android.os.CancellationSignal, android.service.autofill.FillCallback);
+ method public abstract deprecated void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
+ method public void onSaveRequest(android.service.autofill.SaveRequest, android.service.autofill.SaveCallback);
+ method public abstract deprecated void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillService";
field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
}
@@ -37224,6 +37235,25 @@
method public void onSuccess(android.service.autofill.FillResponse);
}
+ public final class FillContext implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getRequestId();
+ method public android.app.assist.AssistStructure getStructure();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillContext> CREATOR;
+ }
+
+ public final class FillRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getClientState();
+ method public int getFlags();
+ method public int getId();
+ method public android.app.assist.AssistStructure getStructure();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillRequest> CREATOR;
+ field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
+ }
+
public final class FillResponse implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -37235,7 +37265,8 @@
method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset);
method public android.service.autofill.FillResponse build();
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 setExtras(android.os.Bundle);
+ method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle);
+ method public deprecated android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
}
@@ -37264,6 +37295,14 @@
method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
}
+ public final class SaveRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getClientState();
+ method public java.util.List<android.service.autofill.FillContext> getFillContexts();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
+ }
+
}
package android.service.carrier {
@@ -48184,7 +48223,7 @@
field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
field public static final java.lang.String EXTRA_DATA_EXTRAS = "android.view.autofill.extra.DATA_EXTRAS";
- field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
+ field public static final deprecated int FLAG_MANUAL_REQUEST = 1; // 0x1
}
public static abstract class AutofillManager.AutofillCallback {
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 334e88b..582709c 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -16,6 +16,7 @@
package android.bluetooth;
+import android.app.PendingIntent;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.le.AdvertiseSettings;
@@ -47,6 +48,9 @@
void unregisterScanner(in int scannerId);
void startScan(in int scannerId, in ScanSettings settings, in List<ScanFilter> filters,
in WorkSource workSource, in List scanStorages, in String callingPackage);
+ void startScanForIntent(in PendingIntent intent, in ScanSettings settings, in List<ScanFilter> filters,
+ in String callingPackage);
+ void stopScanForIntent(in PendingIntent intent, in String callingPackage);
void stopScan(in int scannerId);
void flushPendingBatchResults(in int scannerId);
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index b63c614..b65a7ad 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -17,17 +17,18 @@
package android.bluetooth.le;
import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.app.ActivityThread;
+import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothManager;
-import android.bluetooth.le.IScannerCallback;
import android.os.Handler;
import android.os.Looper;
-import android.os.ParcelUuid;
import android.os.RemoteException;
import android.os.WorkSource;
import android.util.Log;
@@ -36,7 +37,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.UUID;
/**
* This class provides methods to perform scan related operations for Bluetooth LE devices. An
@@ -57,6 +57,27 @@
private static final boolean DBG = true;
private static final boolean VDBG = false;
+ /**
+ * Extra containing a list of ScanResults. It can have one or more results if there was no
+ * error. In case of error, {@link #EXTRA_ERROR_CODE} will contain the error code and this
+ * extra will not be available.
+ */
+ public static final String EXTRA_LIST_SCAN_RESULT
+ = "android.bluetooth.le.extra.LIST_SCAN_RESULT";
+
+ /**
+ * Optional extra indicating the error code, if any. The error code will be one of the
+ * SCAN_FAILED_* codes in {@link ScanCallback}.
+ */
+ public static final String EXTRA_ERROR_CODE = "android.bluetooth.le.extra.ERROR_CODE";
+
+ /**
+ * Optional extra indicating the callback type, which will be one of
+ * ScanSettings.CALLBACK_TYPE_*.
+ * @see ScanCallback#onScanResult(int, ScanResult)
+ */
+ public static final String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE";
+
private final IBluetoothManager mBluetoothManager;
private final Handler mHandler;
private BluetoothAdapter mBluetoothAdapter;
@@ -110,7 +131,27 @@
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public void startScan(List<ScanFilter> filters, ScanSettings settings,
final ScanCallback callback) {
- startScan(filters, settings, null, callback, null);
+ startScan(filters, settings, null, callback, /*callbackIntent=*/ null, null);
+ }
+
+ /**
+ * Start Bluetooth LE scan using a {@link PendingIntent}. The scan results will be delivered via
+ * the PendingIntent. Use this method of scanning if your process is not always running and it
+ * should be started when scan results are available.
+ *
+ * @param filters Optional list of ScanFilters for finding exact BLE devices.
+ * @param settings Optional settings for the scan.
+ * @param callbackIntent The PendingIntent to deliver the result to.
+ * @return Returns 0 for success or an error code from {@link ScanCallback} if the scan request
+ * could not be sent.
+ * @see #stopScan(PendingIntent)
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ public int startScan(@Nullable List<ScanFilter> filters, @Nullable ScanSettings settings,
+ @NonNull PendingIntent callbackIntent) {
+ return startScan(filters,
+ settings != null ? settings : new ScanSettings.Builder().build(),
+ null, null, callbackIntent, null);
}
/**
@@ -145,23 +186,23 @@
Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS })
public void startScanFromSource(List<ScanFilter> filters, ScanSettings settings,
final WorkSource workSource, final ScanCallback callback) {
- startScan(filters, settings, workSource, callback, null);
+ startScan(filters, settings, workSource, callback, null, null);
}
- private void startScan(List<ScanFilter> filters, ScanSettings settings,
+ private int startScan(List<ScanFilter> filters, ScanSettings settings,
final WorkSource workSource, final ScanCallback callback,
+ final PendingIntent callbackIntent,
List<List<ResultStorageDescriptor>> resultStorages) {
BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
- if (callback == null) {
+ if (callback == null && callbackIntent == null) {
throw new IllegalArgumentException("callback is null");
}
if (settings == null) {
throw new IllegalArgumentException("settings is null");
}
synchronized (mLeScanClients) {
- if (mLeScanClients.containsKey(callback)) {
+ if (callback != null && mLeScanClients.containsKey(callback)) {
postCallbackError(callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED);
- return;
}
IBluetoothGatt gatt;
try {
@@ -170,28 +211,34 @@
gatt = null;
}
if (gatt == null) {
- postCallbackError(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
- return;
+ return postCallbackErrorOrReturn(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
}
if (!isSettingsConfigAllowedForScan(settings)) {
- postCallbackError(callback,
- ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
- return;
+ return postCallbackErrorOrReturn(callback,
+ ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
}
if (!isHardwareResourcesAvailableForScan(settings)) {
- postCallbackError(callback,
- ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES);
- return;
+ return postCallbackErrorOrReturn(callback,
+ ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES);
}
if (!isSettingsAndFilterComboAllowed(settings, filters)) {
- postCallbackError(callback,
+ return postCallbackErrorOrReturn(callback,
ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
- return;
}
- BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters,
- settings, workSource, callback, resultStorages);
- wrapper.startRegisteration();
+ if (callback != null) {
+ BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters,
+ settings, workSource, callback, resultStorages);
+ wrapper.startRegistration();
+ } else {
+ try {
+ gatt.startScanForIntent(callbackIntent, settings, filters,
+ ActivityThread.currentOpPackageName());
+ } catch (RemoteException e) {
+ return ScanCallback.SCAN_FAILED_INTERNAL_ERROR;
+ }
+ }
}
+ return ScanCallback.NO_ERROR;
}
/**
@@ -215,6 +262,25 @@
}
/**
+ * Stops an ongoing Bluetooth LE scan started using a PendingIntent.
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+ *
+ * @param callbackIntent The PendingIntent that was used to start the scan.
+ * @see #startScan(List, ScanSettings, PendingIntent)
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ public void stopScan(PendingIntent callbackIntent) {
+ BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
+ IBluetoothGatt gatt;
+ try {
+ gatt = mBluetoothManager.getBluetoothGatt();
+ gatt.stopScanForIntent(callbackIntent, ActivityThread.currentOpPackageName());
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Flush pending batch scan results stored in Bluetooth controller. This will return Bluetooth
* LE scan results batched on bluetooth controller. Returns immediately, batch scan results data
* will be delivered through the {@code callback}.
@@ -252,7 +318,7 @@
scanFilters.add(filter.getFilter());
scanStorages.add(filter.getStorageDescriptors());
}
- startScan(scanFilters, settings, null, callback, scanStorages);
+ startScan(scanFilters, settings, null, callback, null, scanStorages);
}
/**
@@ -295,7 +361,7 @@
mResultStorages = resultStorages;
}
- public void startRegisteration() {
+ public void startRegistration() {
synchronized (this) {
// Scan stopped.
if (mScannerId == -1) return;
@@ -399,7 +465,6 @@
mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
}
});
-
}
@Override
@@ -453,6 +518,15 @@
}
}
+ private int postCallbackErrorOrReturn(final ScanCallback callback, final int errorCode) {
+ if (callback == null) {
+ return errorCode;
+ } else {
+ postCallbackError(callback, errorCode);
+ return ScanCallback.NO_ERROR;
+ }
+ }
+
private void postCallbackError(final ScanCallback callback, final int errorCode) {
mHandler.post(new Runnable() {
@Override
diff --git a/core/java/android/bluetooth/le/ScanCallback.java b/core/java/android/bluetooth/le/ScanCallback.java
index 61b2e78..aff2e90 100644
--- a/core/java/android/bluetooth/le/ScanCallback.java
+++ b/core/java/android/bluetooth/le/ScanCallback.java
@@ -50,6 +50,8 @@
*/
public static final int SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES = 5;
+ static final int NO_ERROR = 0;
+
/**
* Callback when a BLE advertisement has been found.
*
diff --git a/core/java/android/net/MatchAllNetworkSpecifier.java b/core/java/android/net/MatchAllNetworkSpecifier.java
new file mode 100644
index 0000000..7aafc93
--- /dev/null
+++ b/core/java/android/net/MatchAllNetworkSpecifier.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * MatchAllNetworkSpecifier is a marker class used by NetworkFactory classes to indicate
+ * that they accept (match) any network specifier in requests.
+ *
+ * The class must never be used as part of a network request (those semantics aren't specified).
+ *
+ * @hide
+ */
+public final class MatchAllNetworkSpecifier extends NetworkSpecifier implements Parcelable {
+ /**
+ * Utility method which verifies that the ns argument is not a MatchAllNetworkSpecifier and
+ * throws an IllegalArgumentException if it is.
+ */
+ public static void checkNotMatchAllNetworkSpecifier(NetworkSpecifier ns) {
+ if (ns instanceof MatchAllNetworkSpecifier) {
+ throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted");
+ }
+ }
+
+ public boolean satisfiedBy(NetworkSpecifier other) {
+ /*
+ * The method is called by a NetworkRequest to see if it is satisfied by a proposed
+ * network (e.g. as offered by a network factory). Since MatchAllNetweorkSpecifier must
+ * not be used in network requests this method should never be called.
+ */
+ throw new IllegalStateException(
+ "MatchAllNetworkSpecifier must not be used in NetworkRequests");
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof MatchAllNetworkSpecifier;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ // Nothing to write.
+ }
+
+ public static final Parcelable.Creator<MatchAllNetworkSpecifier> CREATOR =
+ new Parcelable.Creator<MatchAllNetworkSpecifier>() {
+ public MatchAllNetworkSpecifier createFromParcel(Parcel in) {
+ return new MatchAllNetworkSpecifier();
+ }
+ public MatchAllNetworkSpecifier[] newArray(int size) {
+ return new MatchAllNetworkSpecifier[size];
+ }
+ };
+}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index a594bef..afca0b0 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -18,9 +18,11 @@
import android.os.Parcel;
import android.os.Parcelable;
-import android.text.TextUtils;
+
import com.android.internal.util.BitUtils;
+import java.util.Objects;
+
/**
* This class represents the capabilities of a network. This is used both to specify
* needs to {@link ConnectivityManager} and when inspecting a network.
@@ -33,6 +35,8 @@
* all cellular based connections are metered and all Wi-Fi based connections are not.
*/
public final class NetworkCapabilities implements Parcelable {
+ private static final String TAG = "NetworkCapabilities";
+
/**
* @hide
*/
@@ -205,19 +209,6 @@
(1 << NET_CAPABILITY_FOREGROUND);
/**
- * Network specifier for factories which want to match any network specifier
- * (NS) in a request. Behavior:
- * <li>Empty NS in request matches any network factory NS</li>
- * <li>Empty NS in the network factory NS only matches a request with an
- * empty NS</li>
- * <li>"*" (this constant) NS in the network factory matches requests with
- * any NS</li>
- *
- * @hide
- */
- public static final String MATCH_ALL_REQUESTS_NETWORK_SPECIFIER = "*";
-
- /**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
* NetworkFactory / NetworkAgent model does not deal well with the situation where a
* capability's presence cannot be known in advance. If such a capability is requested, then we
@@ -579,63 +570,56 @@
this.mLinkDownBandwidthKbps == nc.mLinkDownBandwidthKbps);
}
- private String mNetworkSpecifier;
+ private NetworkSpecifier mNetworkSpecifier = null;
+
/**
* Sets the optional bearer specific network specifier.
* This has no meaning if a single transport is also not specified, so calling
* this without a single transport set will generate an exception, as will
* subsequently adding or removing transports after this is set.
* </p>
- * The interpretation of this {@code String} is bearer specific and bearers that use
- * it should document their particulars. For example, Bluetooth may use some sort of
- * device id while WiFi could used SSID and/or BSSID. Cellular may use carrier SPN (name)
- * or Subscription ID.
*
- * @param networkSpecifier An {@code String} of opaque format used to specify the bearer
- * specific network specifier where the bearer has a choice of
- * networks.
+ * @param networkSpecifier A concrete, parcelable framework class that extends
+ * NetworkSpecifier.
* @return This NetworkCapabilities instance, to facilitate chaining.
* @hide
*/
- public NetworkCapabilities setNetworkSpecifier(String networkSpecifier) {
- if (TextUtils.isEmpty(networkSpecifier) == false && Long.bitCount(mTransportTypes) != 1) {
+ public NetworkCapabilities setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
+ if (networkSpecifier != null && Long.bitCount(mTransportTypes) != 1) {
throw new IllegalStateException("Must have a single transport specified to use " +
"setNetworkSpecifier");
}
+
mNetworkSpecifier = networkSpecifier;
+
return this;
}
/**
* Gets the optional bearer specific network specifier.
*
- * @return The optional {@code String} specifying the bearer specific network specifier.
- * See {@link #setNetworkSpecifier}.
+ * @return The optional {@link NetworkSpecifier} specifying the bearer specific network
+ * specifier. See {@link #setNetworkSpecifier}.
* @hide
*/
- public String getNetworkSpecifier() {
+ public NetworkSpecifier getNetworkSpecifier() {
return mNetworkSpecifier;
}
private void combineSpecifiers(NetworkCapabilities nc) {
- String otherSpecifier = nc.getNetworkSpecifier();
- if (TextUtils.isEmpty(otherSpecifier)) return;
- if (TextUtils.isEmpty(mNetworkSpecifier) == false) {
+ if (mNetworkSpecifier != null && !mNetworkSpecifier.equals(nc.mNetworkSpecifier)) {
throw new IllegalStateException("Can't combine two networkSpecifiers");
}
- setNetworkSpecifier(otherSpecifier);
+ setNetworkSpecifier(nc.mNetworkSpecifier);
}
+
private boolean satisfiedBySpecifier(NetworkCapabilities nc) {
- return (TextUtils.isEmpty(mNetworkSpecifier) ||
- mNetworkSpecifier.equals(nc.mNetworkSpecifier) ||
- MATCH_ALL_REQUESTS_NETWORK_SPECIFIER.equals(nc.mNetworkSpecifier));
+ return mNetworkSpecifier == null || mNetworkSpecifier.satisfiedBy(nc.mNetworkSpecifier)
+ || nc.mNetworkSpecifier instanceof MatchAllNetworkSpecifier;
}
+
private boolean equalsSpecifier(NetworkCapabilities nc) {
- if (TextUtils.isEmpty(mNetworkSpecifier)) {
- return TextUtils.isEmpty(nc.mNetworkSpecifier);
- } else {
- return mNetworkSpecifier.equals(nc.mNetworkSpecifier);
- }
+ return Objects.equals(mNetworkSpecifier, nc.mNetworkSpecifier);
}
/**
@@ -797,7 +781,7 @@
((int)(mTransportTypes >> 32) * 7) +
(mLinkUpBandwidthKbps * 11) +
(mLinkDownBandwidthKbps * 13) +
- (TextUtils.isEmpty(mNetworkSpecifier) ? 0 : mNetworkSpecifier.hashCode() * 17) +
+ Objects.hashCode(mNetworkSpecifier) * 17 +
(mSignalStrength * 19));
}
@@ -811,7 +795,7 @@
dest.writeLong(mTransportTypes);
dest.writeInt(mLinkUpBandwidthKbps);
dest.writeInt(mLinkDownBandwidthKbps);
- dest.writeString(mNetworkSpecifier);
+ dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
dest.writeInt(mSignalStrength);
}
@@ -825,7 +809,7 @@
netCap.mTransportTypes = in.readLong();
netCap.mLinkUpBandwidthKbps = in.readInt();
netCap.mLinkDownBandwidthKbps = in.readInt();
- netCap.mNetworkSpecifier = in.readString();
+ netCap.mNetworkSpecifier = in.readParcelable(null);
netCap.mSignalStrength = in.readInt();
return netCap;
}
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index cb78009..95a8bb4 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import java.util.Objects;
@@ -259,10 +260,27 @@
* networks.
*/
public Builder setNetworkSpecifier(String networkSpecifier) {
- if (NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER.equals(networkSpecifier)) {
- throw new IllegalArgumentException("Invalid network specifier - must not be '"
- + NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER + "'");
- }
+ /*
+ * A StringNetworkSpecifier does not accept null or empty ("") strings. When network
+ * specifiers were strings a null string and an empty string were considered equivalent.
+ * Hence no meaning is attached to a null or empty ("") string.
+ */
+ return setNetworkSpecifier(TextUtils.isEmpty(networkSpecifier) ? null
+ : new StringNetworkSpecifier(networkSpecifier));
+ }
+
+ /**
+ * Sets the optional bearer specific network specifier.
+ * This has no meaning if a single transport is also not specified, so calling
+ * this without a single transport set will generate an exception, as will
+ * subsequently adding or removing transports after this is set.
+ * </p>
+ *
+ * @param networkSpecifier A concrete, parcelable framework class that extends
+ * NetworkSpecifier.
+ */
+ public Builder setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
+ MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(networkSpecifier);
mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
return this;
}
diff --git a/core/java/android/net/NetworkSpecifier.java b/core/java/android/net/NetworkSpecifier.java
new file mode 100644
index 0000000..87a2b05
--- /dev/null
+++ b/core/java/android/net/NetworkSpecifier.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+/**
+ * Describes specific properties of a network for use in a {@link NetworkRequest}.
+ *
+ * Applications cannot instantiate this class by themselves, but can obtain instances of
+ * subclasses of this class via other APIs.
+ */
+public abstract class NetworkSpecifier {
+ /** @hide */
+ public NetworkSpecifier() {}
+
+ /**
+ * Returns true if a request with this {@link NetworkSpecifier} is satisfied by a network
+ * with the given NetworkSpecifier.
+ *
+ * @hide
+ */
+ public abstract boolean satisfiedBy(NetworkSpecifier other);
+}
diff --git a/core/java/android/net/StringNetworkSpecifier.java b/core/java/android/net/StringNetworkSpecifier.java
new file mode 100644
index 0000000..cb7f6bf
--- /dev/null
+++ b/core/java/android/net/StringNetworkSpecifier.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/** @hide */
+public final class StringNetworkSpecifier extends NetworkSpecifier implements Parcelable {
+ /**
+ * Arbitrary string used to pass (additional) information to the network factory.
+ */
+ public final String specifier;
+
+ public StringNetworkSpecifier(String specifier) {
+ Preconditions.checkStringNotEmpty(specifier);
+ this.specifier = specifier;
+ }
+
+ @Override
+ public boolean satisfiedBy(NetworkSpecifier other) {
+ return equals(other);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof StringNetworkSpecifier)) return false;
+ return TextUtils.equals(specifier, ((StringNetworkSpecifier) o).specifier);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(specifier);
+ }
+
+ @Override
+ public String toString() {
+ return specifier;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(specifier);
+ }
+
+ public static final Parcelable.Creator<StringNetworkSpecifier> CREATOR =
+ new Parcelable.Creator<StringNetworkSpecifier>() {
+ public StringNetworkSpecifier createFromParcel(Parcel in) {
+ return new StringNetworkSpecifier(in.readString());
+ }
+ public StringNetworkSpecifier[] newArray(int size) {
+ return new StringNetworkSpecifier[size];
+ }
+ };
+}
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index a4d3fb2..5e49b8f 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -34,6 +34,8 @@
import com.android.internal.os.SomeArgs;
+import java.util.List;
+
//TODO(b/33197203): improve javadoc (of both class and methods); in particular, make sure the
//life-cycle (and how state could be maintained on server-side) is well documented.
@@ -103,24 +105,22 @@
}
@Override
- public void onFillRequest(AssistStructure structure, Bundle extras,
- IFillCallback callback, int flags) {
+ public void onFillRequest(FillRequest request, IFillCallback callback) {
ICancellationSignal transport = CancellationSignal.createTransport();
try {
callback.onCancellable(transport);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
- mHandlerCaller.obtainMessageIIOOOO(MSG_ON_FILL_REQUEST, flags, UNUSED_ARG, structure,
- CancellationSignal.fromTransport(transport), extras, callback)
+ mHandlerCaller.obtainMessageOOO(MSG_ON_FILL_REQUEST, request,
+ CancellationSignal.fromTransport(transport), callback)
.sendToTarget();
}
@Override
- public void onSaveRequest(AssistStructure structure, Bundle extras,
- ISaveCallback callback) {
- mHandlerCaller.obtainMessageOOO(MSG_ON_SAVE_REQUEST, structure,
- extras, callback).sendToTarget();
+ public void onSaveRequest(SaveRequest request, ISaveCallback callback) {
+ mHandlerCaller.obtainMessageOO(MSG_ON_SAVE_REQUEST, request,
+ callback).sendToTarget();
}
};
@@ -131,23 +131,20 @@
break;
} case MSG_ON_FILL_REQUEST: {
final SomeArgs args = (SomeArgs) msg.obj;
- final AssistStructure structure = (AssistStructure) args.arg1;
+ final FillRequest request = (FillRequest) args.arg1;
final CancellationSignal cancellation = (CancellationSignal) args.arg2;
- final Bundle extras = (Bundle) args.arg3;
- final IFillCallback callback = (IFillCallback) args.arg4;
- final FillCallback fillCallback = new FillCallback(callback);
- final int flags = msg.arg1;
+ final IFillCallback callback = (IFillCallback) args.arg3;
+ final FillCallback fillCallback = new FillCallback(callback, request.getId());
args.recycle();
- onFillRequest(structure, extras, flags, cancellation, fillCallback);
+ onFillRequest(request, cancellation, fillCallback);
break;
} case MSG_ON_SAVE_REQUEST: {
final SomeArgs args = (SomeArgs) msg.obj;
- final AssistStructure structure = (AssistStructure) args.arg1;
- final Bundle extras = (Bundle) args.arg2;
- final ISaveCallback callback = (ISaveCallback) args.arg3;
+ final SaveRequest request = (SaveRequest) args.arg1;
+ final ISaveCallback callback = (ISaveCallback) args.arg2;
final SaveCallback saveCallback = new SaveCallback(callback);
args.recycle();
- onSaveRequest(structure, extras, saveCallback);
+ onSaveRequest(request, saveCallback);
break;
} case MSG_DISCONNECT: {
onDisconnected();
@@ -198,6 +195,28 @@
* or {@link FillCallback#onFailure(CharSequence)})
* to notify the result of the request.
*
+ * @param request the {@link FillRequest request} to handle.
+ * See {@link FillResponse} for examples of multiple-sections requests.
+ * @param cancellationSignal signal for observing cancellation requests. The system will use
+ * this to notify you that the fill result is no longer needed and you should stop
+ * handling this fill request in order to save resources.
+ * @param callback object used to notify the result of the request.
+ */
+ public void onFillRequest(@NonNull FillRequest request,
+ @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback) {
+ onFillRequest(request.getStructure(), request.getClientState(), request.getFlags(),
+ cancellationSignal, callback);
+ }
+
+ /**
+ * Called by the Android system do decide if an {@link Activity} can be autofilled by the
+ * service.
+ *
+ * <p>Service must call one of the {@link FillCallback} methods (like
+ * {@link FillCallback#onSuccess(FillResponse)}
+ * or {@link FillCallback#onFailure(CharSequence)})
+ * to notify the result of the request.
+ *
* @param structure {@link Activity}'s view structure.
* @param data bundle containing data passed by the service in a last call to
* {@link FillResponse.Builder#setExtras(Bundle)}, if any. This bundle allows your
@@ -211,6 +230,7 @@
* handling this fill request in order to save resources.
* @param callback object used to notify the result of the request.
*/
+ @Deprecated
public abstract void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle data,
int flags, @NonNull CancellationSignal cancellationSignal,
@NonNull FillCallback callback);
@@ -222,6 +242,23 @@
* {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)})
* to notify the result of the request.
*
+ * @param request the {@link SaveRequest request} to handle.
+ * See {@link FillResponse} for examples of multiple-sections requests.
+ * @param callback object used to notify the result of the request.
+ */
+ public void onSaveRequest(@NonNull SaveRequest request, @NonNull SaveCallback callback) {
+ List<FillContext> contexts = request.getFillContexts();
+ onSaveRequest(contexts.get(contexts.size() - 1).getStructure(),
+ request.getClientState(), callback);
+ }
+
+ /**
+ * Called when user requests service to save the fields of an {@link Activity}.
+ *
+ * <p>Service must call one of the {@link SaveCallback} methods (like
+ * {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)})
+ * to notify the result of the request.
+ *
* @param structure {@link Activity}'s view structure.
* @param data bundle containing data passed by the service in a last call to
* {@link FillResponse.Builder#setExtras(Bundle)}, if any. This bundle allows your
@@ -231,6 +268,7 @@
* See {@link FillResponse} for examples of multiple-sections requests.
* @param callback object used to notify the result of the request.
*/
+ @Deprecated
public abstract void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle data,
@NonNull SaveCallback callback);
diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java
index e8ad14f..a009be8 100644
--- a/core/java/android/service/autofill/FillCallback.java
+++ b/core/java/android/service/autofill/FillCallback.java
@@ -27,11 +27,13 @@
*/
public final class FillCallback {
private final IFillCallback mCallback;
+ private final int mRequestId;
private boolean mCalled;
/** @hide */
- public FillCallback(IFillCallback callback) {
+ public FillCallback(IFillCallback callback, int requestId) {
mCallback = callback;
+ mRequestId = requestId;
}
/**
@@ -47,7 +49,7 @@
assertNotCalled();
mCalled = true;
try {
- mCallback.onSuccess(response);
+ mCallback.onSuccess(response, mRequestId);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/service/autofill/FillContext.java b/core/java/android/service/autofill/FillContext.java
new file mode 100644
index 0000000..2efa08c
--- /dev/null
+++ b/core/java/android/service/autofill/FillContext.java
@@ -0,0 +1,96 @@
+/*
+ * 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.service.autofill;
+
+import android.annotation.NonNull;
+import android.app.assist.AssistStructure;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class represents a context for each fill request made via {@link
+ * AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)}.
+ * It contains a snapshot of the UI state, the view ids that were returned by
+ * the {@link AutofillService autofill service} as both required to trigger a save
+ * and optional that can be saved, and the id of the corresponding {@link
+ * FillRequest}.
+ * <p>
+ * This context allows you to inspect the values for the interesting views
+ * in the context they appeared. Also a reference to the corresponding fill
+ * request is useful to store meta-data in the client state bundle passed
+ * to {@link FillResponse.Builder#setClientState(Bundle)} to avoid interpreting
+ * the UI state again while saving.
+ */
+public final class FillContext implements Parcelable {
+ private final int mRequestId;
+ private final @NonNull AssistStructure mStructure;
+
+ /** @hide */
+ public FillContext(int requestId, @NonNull AssistStructure structure) {
+ mRequestId = requestId;
+ mStructure = structure;
+ }
+
+ private FillContext(Parcel parcel) {
+ this(parcel.readInt(), parcel.readParcelable(null));
+ }
+
+ /**
+ * Gets the id of the {@link FillRequest fill request} this context
+ * corresponds to. This is useful to associate your custom client
+ * state with every request to avoid reinterpreting the UI when saving
+ * user data.
+ *
+ * @return The request id.
+ */
+ public int getRequestId() {
+ return mRequestId;
+ }
+
+ /**
+ * @return The screen content.
+ */
+ public AssistStructure getStructure() {
+ return mStructure;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mRequestId);
+ parcel.writeParcelable(mStructure, flags);
+ }
+
+ public static final Parcelable.Creator<FillContext> CREATOR =
+ new Parcelable.Creator<FillContext>() {
+ @Override
+ public FillContext createFromParcel(Parcel parcel) {
+ return new FillContext(parcel);
+ }
+
+ @Override
+ public FillContext[] newArray(int size) {
+ return new FillContext[size];
+ }
+ };
+}
diff --git a/core/java/android/service/autofill/FillRequest.aidl b/core/java/android/service/autofill/FillRequest.aidl
new file mode 100644
index 0000000..2b1a8fe
--- /dev/null
+++ b/core/java/android/service/autofill/FillRequest.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.service.autofill;
+
+parcelable FillRequest;
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
new file mode 100644
index 0000000..aa6db4d
--- /dev/null
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.service.autofill;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.assist.AssistStructure;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Parcel;
+import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * This class represents a request to an {@link AutofillService autofill provider}
+ * to interpret the screen and provide information to the system which views are
+ * interesting for saving and what are the possible ways to fill the inputs on
+ * the screen if applicable.
+ *
+ * @see AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)
+ */
+public final class FillRequest implements Parcelable {
+ private static AtomicInteger sIdCounter = new AtomicInteger();
+
+ /**
+ * Indicates autofill was explicitly requested by the user.
+ */
+ public static final int FLAG_MANUAL_REQUEST = 0x1;
+
+ /** @hide */
+ @IntDef(
+ flag = true,
+ value = {FLAG_MANUAL_REQUEST})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface RequestFlags{}
+
+ private final int mId;
+ private final @RequestFlags int mFlags;
+ private final @NonNull AssistStructure mStructure;
+ private final @Nullable Bundle mClientState;
+
+ /** @hide */
+ public FillRequest(@NonNull AssistStructure structure,
+ @Nullable Bundle clientState, @RequestFlags int flags) {
+ this(sIdCounter.incrementAndGet(), structure, clientState, flags);
+ }
+
+ private FillRequest(@NonNull Parcel parcel) {
+ mId = parcel.readInt();
+ mStructure = parcel.readParcelable(null);
+ mClientState = parcel.readBundle();
+ mFlags = parcel.readInt();
+ }
+
+ private FillRequest(int id, @NonNull AssistStructure structure,
+ @Nullable Bundle clientState, @RequestFlags int flags) {
+ mId = id;
+ mFlags = Preconditions.checkFlagsArgument(flags, FLAG_MANUAL_REQUEST);
+ mStructure = Preconditions.checkNotNull(structure, "structure");
+ mClientState = clientState;
+ }
+
+ /**
+ * @return The unique id of this request.
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * @return The flags associated with this request.
+ *
+ * @see #FLAG_MANUAL_REQUEST
+ */
+ public @RequestFlags int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * @return The structure capturing the UI state.
+ */
+ public @NonNull AssistStructure getStructure() {
+ return mStructure;
+ }
+
+ /**
+ * Gets the extra client state returned from the last {@link
+ * AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)
+ * fill request}.
+ * <p>
+ * Once a {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)
+ * save request} is made the client state is cleared.
+ *
+ * @return The client state.
+ */
+ public @Nullable Bundle getClientState() {
+ return mClientState;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mId);
+ parcel.writeParcelable(mStructure, flags);
+ parcel.writeBundle(mClientState);
+ parcel.writeInt(mFlags);
+ }
+
+ public static final Parcelable.Creator<FillRequest> CREATOR =
+ new Parcelable.Creator<FillRequest>() {
+ @Override
+ public FillRequest createFromParcel(Parcel parcel) {
+ return new FillRequest(parcel);
+ }
+
+ @Override
+ public FillRequest[] newArray(int size) {
+ return new FillRequest[size];
+ }
+ };
+}
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index eab0d4c..8c8060a 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -132,25 +132,25 @@
*/
public final class FillResponse implements Parcelable {
- private final ArrayList<Dataset> mDatasets;
- private final SaveInfo mSaveInfo;
- private final Bundle mExtras;
- private final RemoteViews mPresentation;
- private final IntentSender mAuthentication;
- private AutofillId[] mAuthenticationIds;
+ private final @Nullable ArrayList<Dataset> mDatasets;
+ private final @Nullable SaveInfo mSaveInfo;
+ private final @Nullable Bundle mClientState;
+ private final @Nullable RemoteViews mPresentation;
+ private final @Nullable IntentSender mAuthentication;
+ private final @Nullable AutofillId[] mAuthenticationIds;
private FillResponse(@NonNull Builder builder) {
mDatasets = builder.mDatasets;
mSaveInfo = builder.mSaveInfo;
- mExtras = builder.mExtras;
+ mClientState = builder.mCLientState;
mPresentation = builder.mPresentation;
mAuthentication = builder.mAuthentication;
mAuthenticationIds = builder.mAuthenticationIds;
}
/** @hide */
- public @Nullable Bundle getExtras() {
- return mExtras;
+ public @Nullable Bundle getClientState() {
+ return mClientState;
}
/** @hide */
@@ -185,7 +185,7 @@
public static final class Builder {
private ArrayList<Dataset> mDatasets;
private SaveInfo mSaveInfo;
- private Bundle mExtras;
+ private Bundle mCLientState;
private RemoteViews mPresentation;
private IntentSender mAuthentication;
private AutofillId[] mAuthenticationIds;
@@ -289,23 +289,35 @@
return this;
}
+ @Deprecated
+ public Builder setExtras(@Nullable Bundle extras) {
+ throwIfDestroyed();
+ mCLientState = extras;
+ return this;
+ }
+
/**
- * Sets a {@link Bundle} that will be passed to subsequent APIs that
+ * Sets a {@link Bundle state} that will be passed to subsequent APIs that
* manipulate this response. For example, they are passed to subsequent
* calls to {@link AutofillService#onFillRequest(
* android.app.assist.AssistStructure, Bundle, int,
* android.os.CancellationSignal, FillCallback)} and {@link AutofillService#onSaveRequest(
- * android.app.assist.AssistStructure, Bundle, SaveCallback)}.
+ * android.app.assist.AssistStructure, Bundle, SaveCallback)}. You can use
+ * this to store intermediate state that is persistent across multiple
+ * fill requests and the subsequent save request.
*
* <p>If this method is called on multiple {@link FillResponse} objects for the same
* activity, just the latest bundle is passed back to the service.
*
- * @param extras The response extras.
+ * <p>Once a {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)
+ * save request} is made the client state is cleared.
+ *
+ * @param clientState The custom client state.
* @return This builder.
*/
- public Builder setExtras(Bundle extras) {
+ public Builder setClientState(@Nullable Bundle clientState) {
throwIfDestroyed();
- mExtras = extras;
+ mCLientState = clientState;
return this;
}
@@ -344,7 +356,7 @@
return new StringBuilder(
"FillResponse: [datasets=").append(mDatasets)
.append(", saveInfo=").append(mSaveInfo)
- .append(", hasExtras=").append(mExtras != null)
+ .append(", clientState=").append(mClientState != null)
.append(", hasPresentation=").append(mPresentation != null)
.append(", hasAuthentication=").append(mAuthentication != null)
.append(", authenticationSize=").append(mAuthenticationIds != null
@@ -365,7 +377,7 @@
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeTypedArrayList(mDatasets, flags);
parcel.writeParcelable(mSaveInfo, flags);
- parcel.writeParcelable(mExtras, flags);
+ parcel.writeParcelable(mClientState, flags);
parcel.writeParcelableArray(mAuthenticationIds, flags);
parcel.writeParcelable(mAuthentication, flags);
parcel.writeParcelable(mPresentation, flags);
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index a8d86ca..23a1a3f 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -16,10 +16,10 @@
package android.service.autofill;
-import android.app.assist.AssistStructure;
-import android.os.Bundle;
+import android.service.autofill.FillRequest;
import android.service.autofill.IFillCallback;
import android.service.autofill.ISaveCallback;
+import android.service.autofill.SaveRequest;
import com.android.internal.os.IResultReceiver;
/**
@@ -29,8 +29,6 @@
*/
oneway interface IAutoFillService {
void onConnectedStateChanged(boolean connected);
- void onFillRequest(in AssistStructure structure, in Bundle extras,
- in IFillCallback callback, int flags);
- void onSaveRequest(in AssistStructure structure, in Bundle extras,
- in ISaveCallback callback);
+ void onFillRequest(in FillRequest request, in IFillCallback callback);
+ void onSaveRequest(in SaveRequest request, in ISaveCallback callback);
}
diff --git a/core/java/android/service/autofill/IFillCallback.aidl b/core/java/android/service/autofill/IFillCallback.aidl
index 2bb3e9a..688ac84 100644
--- a/core/java/android/service/autofill/IFillCallback.aidl
+++ b/core/java/android/service/autofill/IFillCallback.aidl
@@ -27,6 +27,6 @@
*/
interface IFillCallback {
void onCancellable(in ICancellationSignal cancellation);
- void onSuccess(in FillResponse response);
+ void onSuccess(in FillResponse response, int requestId);
void onFailure(CharSequence message);
}
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 4ad0f08..258d257 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -140,7 +140,19 @@
*/
public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 0x10;
- private final int mType;
+ /** @hide */
+ @IntDef(
+ flag = true,
+ value = {
+ SAVE_DATA_TYPE_GENERIC,
+ SAVE_DATA_TYPE_PASSWORD,
+ SAVE_DATA_TYPE_ADDRESS,
+ SAVE_DATA_TYPE_CREDIT_CARD,
+ SAVE_DATA_TYPE_EMAIL_ADDRESS})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface SaveDataType{}
+
+ private final @SaveDataType int mType;
private final CharSequence mNegativeActionTitle;
private final IntentSender mNegativeActionListener;
private final AutofillId[] mRequiredIds;
@@ -177,7 +189,7 @@
}
/** @hide */
- public int getType() {
+ public @SaveDataType int getType() {
return mType;
}
@@ -191,7 +203,7 @@
*/
public static final class Builder {
- private final int mType;
+ private final @SaveDataType int mType;
private CharSequence mNegativeActionTitle;
private IntentSender mNegativeActionListener;
// TODO(b/33197203): make mRequiredIds final once addSavableIds() is gone
@@ -215,7 +227,7 @@
*
* @throws IllegalArgumentException if {@code requiredIds} is {@code null} or empty.
*/
- public Builder(int type, @NonNull AutofillId[] requiredIds) {
+ public Builder(@SaveDataType int type, @NonNull AutofillId[] requiredIds) {
if (false) {// TODO(b/33197203): re-move when clients use it
Preconditions.checkArgument(requiredIds != null && requiredIds.length > 0,
"must have at least one required id: " + Arrays.toString(requiredIds));
@@ -230,7 +242,7 @@
* // TODO(b/33197203): make sure is removed when clients migrated
*/
@Deprecated
- public Builder(int type) {
+ public Builder(@SaveDataType int type) {
this(type, null);
}
diff --git a/core/java/android/service/autofill/SaveRequest.aidl b/core/java/android/service/autofill/SaveRequest.aidl
new file mode 100644
index 0000000..7789b577
--- /dev/null
+++ b/core/java/android/service/autofill/SaveRequest.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.service.autofill;
+
+parcelable SaveRequest;
diff --git a/core/java/android/service/autofill/SaveRequest.java b/core/java/android/service/autofill/SaveRequest.java
new file mode 100644
index 0000000..9de9315
--- /dev/null
+++ b/core/java/android/service/autofill/SaveRequest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.service.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Parcel;
+import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents a request to an {@link AutofillService
+ * autofill provider} to save applicable data entered by the user.
+ *
+ * @see AutofillService#onSaveRequest(SaveRequest, SaveCallback)
+ */
+public final class SaveRequest implements Parcelable {
+ private final @NonNull ArrayList<FillContext> mFillContexts;
+ private final @Nullable Bundle mClientState;
+
+ /** @hide */
+ public SaveRequest(@NonNull ArrayList<FillContext> fillContexts,
+ @Nullable Bundle clientState) {
+ mFillContexts = Preconditions.checkNotNull(fillContexts, "fillContexts");
+ mClientState = clientState;
+ }
+
+ private SaveRequest(@NonNull Parcel parcel) {
+ this(parcel.readTypedArrayList(null), parcel.readBundle());
+ }
+
+ /**
+ * @return The contexts associated with each previous fill request.
+ */
+ public @NonNull List<FillContext> getFillContexts() {
+ return mFillContexts;
+ }
+
+ /**
+ * Gets the extra client state returned from the last {@link
+ * AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)}
+ * fill request}.
+ *
+ * @return The client state.
+ */
+ public @Nullable Bundle getClientState() {
+ return mClientState;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeTypedArrayList(mFillContexts, flags);
+ parcel.writeBundle(mClientState);
+ }
+
+ public static final Creator<SaveRequest> CREATOR =
+ new Creator<SaveRequest>() {
+ @Override
+ public SaveRequest createFromParcel(Parcel parcel) {
+ return new SaveRequest(parcel);
+ }
+
+ @Override
+ public SaveRequest[] newArray(int size) {
+ return new SaveRequest[size];
+ }
+ };
+}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 570f968..ec6559c 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -101,7 +101,10 @@
// Public flags start from the lowest bit
/**
* Indicates autofill was explicitly requested by the user.
+ *
+ * @deprecated Use {@link android.service.autofill.FillRequest#FLAG_MANUAL_REQUEST}
*/
+ @Deprecated
public static final int FLAG_MANUAL_REQUEST = 0x1;
// Private flags start from the highest bit
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 2f12e9b..28d9fcf 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -155,7 +155,7 @@
public static final int IME_ACTION_PREVIOUS = 0x00000007;
/**
- * Flag of {@link #imeOptions}: used to request that the IME does not update any personalized
+ * Flag of {@link #imeOptions}: used to request that the IME should not update any personalized
* data such as typing history and personalized language model based on what the user typed on
* this text editing object. Typical use cases are:
* <ul>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 90ad191..4fb21fa 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1371,6 +1371,22 @@
Corresponds to
{@link android.view.inputmethod.EditorInfo#IME_ACTION_PREVIOUS}. -->
<flag name="actionPrevious" value="0x00000007" />
+ <!-- Used to request that the IME should not update any personalized data such as typing
+ history and personalized language model based on what the user typed on this text
+ editing object. Typical use cases are:
+ <ul>
+ <li>When the application is in a special mode, where user's activities are expected
+ to be not recorded in the application's history. Some web browsers and chat
+ applications may have this kind of modes.</li>
+ <li>When storing typing history does not make much sense. Specifying this flag in
+ typing games may help to avoid typing history from being filled up with words that
+ the user is less likely to type in their daily life. Another example is that when
+ the application already knows that the expected input is not a valid word (e.g. a
+ promotion code that is not a valid word in any natural language).</li>
+ </ul>
+ <p>Applications need to be aware that the flag is not a guarantee, and some IMEs may
+ not respect it.</p> -->
+ <flag name="flagNoPersonalizedLearning" value="0x1000000" />
<!-- Used to request that the IME never go
into fullscreen mode. Applications need to be aware that the flag is not
a guarantee, and not all IMEs will respect it.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index f3fb1ef..ec56e15 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -33,6 +33,8 @@
boolean isPulsingBlocked();
void startPendingIntentDismissingKeyguard(PendingIntent intent);
+ void abortPulsing();
+ void extendPulse();
interface Callback {
default void onNotificationHeadsUp() {}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index f27521e..1cc5fb9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -58,12 +58,15 @@
/** Pulse is done showing. Followed by transition to DOZE or DOZE_AOD. */
DOZE_PULSE_DONE,
/** Doze is done. DozeService is finished. */
- FINISH;
+ FINISH,
+ /** AOD, but the display is temporarily off. */
+ DOZE_AOD_PAUSED;
boolean canPulse() {
switch (this) {
case DOZE:
case DOZE_AOD:
+ case DOZE_AOD_PAUSED:
return true;
default:
return false;
@@ -85,6 +88,7 @@
case UNINITIALIZED:
case INITIALIZED:
case DOZE:
+ case DOZE_AOD_PAUSED:
return Display.STATE_OFF;
case DOZE_PULSING:
case DOZE_AOD:
@@ -241,6 +245,11 @@
if (mState == State.FINISH) {
return State.FINISH;
}
+ if ((mState == State.DOZE_AOD_PAUSED || mState == State.DOZE_AOD || mState == State.DOZE)
+ && requestedState == State.DOZE_PULSE_DONE) {
+ Log.i(TAG, "Dropping pulse done because current state is already done: " + mState);
+ return mState;
+ }
if (requestedState == State.DOZE_REQUEST_PULSE && !mState.canPulse()) {
Log.i(TAG, "Dropping pulse request because current state can't pulse: " + mState);
return mState;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 2ac0657..73f5222 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -22,6 +22,8 @@
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
@@ -40,6 +42,7 @@
import java.io.PrintWriter;
import java.util.List;
+import java.util.function.Consumer;
public class DozeSensors {
@@ -55,18 +58,22 @@
private final DozeParameters mDozeParameters;
private final AmbientDisplayConfiguration mConfig;
private final WakeLock mWakeLock;
+ private final Consumer<Boolean> mProxCallback;
private final Callback mCallback;
private final Handler mHandler = new Handler();
+ private final ProxSensor mProxSensor;
public DozeSensors(Context context, SensorManager sensorManager, DozeParameters dozeParameters,
- AmbientDisplayConfiguration config, WakeLock wakeLock, Callback callback) {
+ AmbientDisplayConfiguration config, WakeLock wakeLock, Callback callback,
+ Consumer<Boolean> proxCallback) {
mContext = context;
mSensorManager = sensorManager;
mDozeParameters = dozeParameters;
mConfig = config;
mWakeLock = wakeLock;
+ mProxCallback = proxCallback;
mResolver = mContext.getContentResolver();
mSensors = new TriggerSensor[] {
@@ -86,6 +93,8 @@
true /* configured */,
DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP)
};
+
+ mProxSensor = new ProxSensor();
mCallback = callback;
}
@@ -129,6 +138,10 @@
}
}
+ public void setProxListening(boolean listen) {
+ mProxSensor.setRegistered(listen);
+ }
+
private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
@@ -152,6 +165,43 @@
}
}
+ private class ProxSensor implements SensorEventListener {
+
+ boolean mRegistered;
+ Boolean mCurrentlyFar;
+
+ void setRegistered(boolean register) {
+ if (mRegistered == register) {
+ // Send an update even if we don't re-register.
+ mHandler.post(() -> {
+ if (mCurrentlyFar != null) {
+ mProxCallback.accept(mCurrentlyFar);
+ }
+ });
+ return;
+ }
+ if (register) {
+ mRegistered = mSensorManager.registerListener(this,
+ mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY),
+ SensorManager.SENSOR_DELAY_NORMAL, mHandler);
+ } else {
+ mSensorManager.unregisterListener(this);
+ mRegistered = false;
+ mCurrentlyFar = null;
+ }
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ mCurrentlyFar = event.values[0] >= event.sensor.getMaximumRange();
+ mProxCallback.accept(mCurrentlyFar);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ }
+
private class TriggerSensor extends TriggerEventListener {
final Sensor mSensor;
final boolean mConfigured;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 1b9bf73..9b3593b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -46,6 +46,7 @@
public class DozeTriggers implements DozeMachine.Part {
private static final String TAG = "DozeTriggers";
+ private static final boolean DEBUG = DozeService.DEBUG;
/** adb shell am broadcast -a com.android.systemui.doze.pulse com.android.systemui */
private static final String PULSE_ACTION = "com.android.systemui.doze.pulse";
@@ -81,7 +82,7 @@
mWakeLock = wakeLock;
mAllowPulseTriggers = allowPulseTriggers;
mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters, config,
- wakeLock, this::onSensor);
+ wakeLock, this::onSensor, this::onProximityFar);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
}
@@ -113,6 +114,22 @@
}
}
+ private void onProximityFar(boolean far) {
+ final boolean near = !far;
+ DozeMachine.State state = mMachine.getState();
+ if (near && state == DozeMachine.State.DOZE_PULSING) {
+ if (DEBUG) Log.i(TAG, "Prox NEAR, ending pulse");
+ mMachine.requestState(DozeMachine.State.DOZE_PULSE_DONE);
+ }
+ if (far && state == DozeMachine.State.DOZE_AOD_PAUSED) {
+ if (DEBUG) Log.i(TAG, "Prox FAR, unpausing AOD");
+ mMachine.requestState(DozeMachine.State.DOZE_AOD);
+ } else if (near && state == DozeMachine.State.DOZE_AOD) {
+ if (DEBUG) Log.i(TAG, "Prox NEAR, pausing AOD");
+ mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSED);
+ }
+ }
+
private void onCarMode() {
mMachine.requestState(DozeMachine.State.FINISH);
}
@@ -131,15 +148,21 @@
break;
case DOZE:
case DOZE_AOD:
+ case DOZE_AOD_PAUSED:
+ mDozeSensors.setProxListening(newState != DozeMachine.State.DOZE);
mDozeSensors.setListening(true);
if (oldState != DozeMachine.State.INITIALIZED) {
mDozeSensors.reregisterAllSensors();
}
break;
+ case DOZE_PULSING:
+ mDozeSensors.setProxListening(true);
+ break;
case FINISH:
mBroadcastReceiver.unregister(mContext);
mDozeHost.removeCallback(mHostCallback);
mDozeSensors.setListening(false);
+ mDozeSensors.setProxListening(false);
break;
default:
}
@@ -156,6 +179,7 @@
private void requestPulse(final int reason, boolean performedProxCheck) {
Assert.isMainThread();
+ mDozeHost.extendPulse();
if (mPulsePending || !mAllowPulseTriggers || !canPulse()) {
return;
}
@@ -286,6 +310,8 @@
}
private class TriggerReceiver extends BroadcastReceiver {
+ private boolean mRegistered;
+
@Override
public void onReceive(Context context, Intent intent) {
if (PULSE_ACTION.equals(intent.getAction())) {
@@ -301,14 +327,22 @@
}
public void register(Context context) {
+ if (mRegistered) {
+ return;
+ }
IntentFilter filter = new IntentFilter(PULSE_ACTION);
filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
filter.addAction(Intent.ACTION_USER_SWITCHED);
context.registerReceiver(this, filter);
+ mRegistered = true;
}
public void unregister(Context context) {
+ if (!mRegistered) {
+ return;
+ }
context.unregisterReceiver(this);
+ mRegistered = false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index f577654..6098a20 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -75,11 +75,14 @@
scheduleTimeTick();
break;
case DOZE:
+ case DOZE_AOD_PAUSED:
unscheduleTimeTick();
break;
case DOZE_REQUEST_PULSE:
pulseWhileDozing(DozeLog.PULSE_REASON_NOTIFICATION /* TODO */);
break;
+ case DOZE_PULSE_DONE:
+ mHost.abortPulsing();
case INITIALIZED:
mHost.startDozing();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index c3f8d97..40776ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -158,6 +158,10 @@
return sPickupSubtypePerformsProxMatcher.isIn(subType);
}
+ public int getPulseVisibleDurationExtended() {
+ return 2 * getPulseVisibleDuration();
+ }
+
/**
* Parses a spec of the form `1,2,3,!5,*`. The resulting object will match numbers that are
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index b78f748..c5f23c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -30,6 +30,7 @@
import com.android.systemui.Interpolators;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
+import com.android.systemui.doze.DozeTriggers;
/**
* Controller which handles all the doze animations of the scrims.
@@ -43,8 +44,6 @@
private final ScrimController mScrimController;
private final Context mContext;
- private final View mStackScroller;
- private final NotificationPanelView mNotificationPanelView;
private boolean mDozing;
private DozeHost.PulseCallback mPulseCallback;
@@ -53,24 +52,22 @@
private Animator mBehindAnimator;
private float mInFrontTarget;
private float mBehindTarget;
+ private boolean mDozingAborted;
- public DozeScrimController(ScrimController scrimController, Context context,
- View stackScroller, NotificationPanelView notificationPanelView) {
+ public DozeScrimController(ScrimController scrimController, Context context) {
mContext = context;
- mStackScroller = stackScroller;
mScrimController = scrimController;
mDozeParameters = new DozeParameters(context);
- mNotificationPanelView = notificationPanelView;
}
public void setDozing(boolean dozing, boolean animate) {
if (mDozing == dozing) return;
mDozing = dozing;
if (mDozing) {
+ mDozingAborted = false;
abortAnimations();
mScrimController.setDozeBehindAlpha(1f);
mScrimController.setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() ? 0f : 1f);
- mNotificationPanelView.setDark(true);
} else {
cancelPulsing();
if (animate) {
@@ -85,8 +82,6 @@
mScrimController.setDozeBehindAlpha(0f);
mScrimController.setDozeInFrontAlpha(0f);
}
- // TODO: animate
- mNotificationPanelView.setDark(false);
}
}
@@ -116,10 +111,19 @@
cancelPulsing();
if (mDozing) {
mScrimController.setDozeBehindAlpha(1f);
- mScrimController.setDozeInFrontAlpha(1f);
+ mScrimController.setDozeInFrontAlpha(
+ mDozeParameters.getAlwaysOn() && !mDozingAborted ? 0f : 1f);
}
}
+ /**
+ * Aborts dozing immediately.
+ */
+ public void abortDoze() {
+ mDozingAborted = true;
+ abortPulsing();
+ }
+
public void onScreenTurnedOn() {
if (isPulsing()) {
final boolean pickupOrDoubleTap = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP
@@ -139,12 +143,17 @@
return mDozing;
}
+ public void extendPulse() {
+ mHandler.removeCallbacks(mPulseOut);
+ }
+
private void cancelPulsing() {
if (DEBUG) Log.d(TAG, "Cancel pulsing");
if (mPulseCallback != null) {
mHandler.removeCallbacks(mPulseIn);
mHandler.removeCallbacks(mPulseOut);
+ mHandler.removeCallbacks(mPulseOutExtended);
pulseFinished();
}
}
@@ -271,12 +280,23 @@
if (DEBUG) Log.d(TAG, "Pulse in finished, mDozing=" + mDozing);
if (!mDozing) return;
mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
+ mHandler.postDelayed(mPulseOutExtended,
+ mDozeParameters.getPulseVisibleDurationExtended());
+ }
+ };
+
+ private final Runnable mPulseOutExtended = new Runnable() {
+ @Override
+ public void run() {
+ mHandler.removeCallbacks(mPulseOut);
+ mPulseOut.run();
}
};
private final Runnable mPulseOut = new Runnable() {
@Override
public void run() {
+ mHandler.removeCallbacks(mPulseOutExtended);
if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
if (!mDozing) return;
startScrimAnimation(true /* inFront */, mDozeParameters.getAlwaysOn() ? 0 : 1,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
index 2bb3cbc..9206914 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -221,7 +221,7 @@
case MODE_WAKE_AND_UNLOCK:
Trace.beginSection("MODE_WAKE_AND_UNLOCK");
mStatusBarWindowManager.setStatusBarFocusable(false);
- mDozeScrimController.abortPulsing();
+ mDozeScrimController.abortDoze();
mKeyguardViewMediator.onWakeAndUnlocking();
mScrimController.setWakeAndUnlocking();
if (mStatusBar.getNavigationBarView() != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index d3cb6a4a8..fbf53e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1114,8 +1114,7 @@
}
mHeadsUpManager.addListener(mScrimController);
mStackScroller.setScrimController(mScrimController);
- mDozeScrimController = new DozeScrimController(mScrimController, context, mStackScroller,
- mNotificationPanel);
+ mDozeScrimController = new DozeScrimController(mScrimController, context);
// Other icons
mVolumeComponent = getComponent(VolumeComponent.class);
@@ -4332,6 +4331,7 @@
mStackScroller.setDark(mDozing, animate, mWakeUpTouchLocation);
mScrimController.setDozing(mDozing);
mKeyguardIndicationController.setDozing(mDozing);
+ mNotificationPanel.setDark(mDozing);
// Immediately abort the dozing from the doze scrim controller in case of wake-and-unlock
// for pulsing so the Keyguard fade-out animation scrim can take over.
@@ -4958,6 +4958,7 @@
mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD
|| mFingerprintUnlockController.getMode()
== FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+ mStatusBarWindowManager.setDozing(mDozing);
updateDozingState();
Trace.endSection();
}
@@ -5065,6 +5066,16 @@
StatusBar.this.startPendingIntentDismissingKeyguard(intent);
}
+ @Override
+ public void abortPulsing() {
+ mDozeScrimController.abortPulsing();
+ }
+
+ @Override
+ public void extendPulse() {
+ mDozeScrimController.extendPulse();
+ }
+
}
// Begin Extra BaseStatusBar methods.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index deb0070..0326e40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -118,7 +118,7 @@
mLpChanged.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
}
- if (state.keyguardShowing && !state.backdropShowing) {
+ if (state.keyguardShowing && !state.backdropShowing && !state.dozing) {
mLpChanged.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
} else {
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
@@ -357,6 +357,11 @@
apply(mCurrentState);
}
+ public void setDozing(boolean dozing) {
+ mCurrentState.dozing = dozing;
+ apply(mCurrentState);
+ }
+
public void setBarHeight(int barHeight) {
mBarHeight = barHeight;
apply(mCurrentState);
@@ -404,6 +409,7 @@
boolean remoteInputActive;
boolean forcePluginOpen;
+ boolean dozing;
private boolean isKeyguardShowingAndNotOccluded() {
return keyguardShowing && !keyguardOccluded;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 1848d4e..1a09d75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -255,6 +255,9 @@
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
mStackScrollLayout.closeControlsIfOutsideTouch(ev);
}
+ if (mService.isDozing()) {
+ mService.mDozeScrimController.extendPulse();
+ }
return super.dispatchTouchEvent(ev);
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 63bf373..57ef6d0 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -48,6 +48,7 @@
import android.provider.Settings;
import android.service.autofill.AutofillService;
import android.service.autofill.AutofillServiceInfo;
+import android.service.autofill.FillRequest;
import android.service.autofill.IAutoFillService;
import android.text.TextUtils;
import android.util.LocalLog;
@@ -169,7 +170,8 @@
structure.sanitizeForParceling(true);
// TODO(b/33197203): Need to pipe the bundle
- session.mRemoteFillService.onFillRequest(structure, null, session.mFlags);
+ FillRequest request = new FillRequest(structure, null, session.mFlags);
+ session.mRemoteFillService.onFillRequest(request);
}
};
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index dd520ac..3badcfc 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -20,12 +20,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.assist.AssistStructure;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.os.Bundle;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.ICancellationSignal;
@@ -33,10 +31,12 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.autofill.AutofillService;
+import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
import android.service.autofill.IAutoFillService;
import android.service.autofill.IFillCallback;
import android.service.autofill.ISaveCallback;
+import android.service.autofill.SaveRequest;
import android.text.format.DateUtils;
import android.util.Slog;
@@ -88,7 +88,7 @@
public interface FillServiceCallbacks {
void onFillRequestSuccess(@Nullable FillResponse response,
- @NonNull String servicePackageName);
+ @NonNull String servicePackageName, int requestId);
void onFillRequestFailure(@Nullable CharSequence message,
@NonNull String servicePackageName);
void onSaveRequestSuccess(@NonNull String servicePackageName);
@@ -134,17 +134,16 @@
mCallbacks.onServiceDied(this);
}
- public void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle extras,
- int flags) {
+ public void onFillRequest(@NonNull FillRequest request) {
cancelScheduledUnbind();
- final PendingFillRequest request = new PendingFillRequest(structure, extras, this, flags);
- mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, request).sendToTarget();
+ final PendingFillRequest pendingRequest = new PendingFillRequest(request, this);
+ mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, pendingRequest).sendToTarget();
}
- public void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle extras) {
+ public void onSaveRequest(@NonNull SaveRequest request) {
cancelScheduledUnbind();
- final PendingSaveRequest request = new PendingSaveRequest(structure, extras, this);
- mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, request).sendToTarget();
+ final PendingSaveRequest pendingRequest = new PendingSaveRequest(request, this);
+ mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, pendingRequest).sendToTarget();
}
// Note: we are dumping without a lock held so this is a bit racy but
@@ -253,10 +252,11 @@
}
private void dispatchOnFillRequestSuccess(PendingRequest pendingRequest,
- FillResponse response) {
+ FillResponse response, int requestId) {
mHandler.getHandler().post(() -> {
if (handleResponseCallbackCommon(pendingRequest)) {
- mCallbacks.onFillRequestSuccess(response, mComponentName.getPackageName());
+ mCallbacks.onFillRequestSuccess(response, mComponentName.getPackageName(),
+ requestId);
}
});
}
@@ -392,18 +392,13 @@
private static final class PendingFillRequest extends PendingRequest {
private final Object mLock = new Object();
private final WeakReference<RemoteFillService> mWeakService;
- private final AssistStructure mStructure;
- private final Bundle mExtras;
+ private final FillRequest mRequest;
private final IFillCallback mCallback;
private ICancellationSignal mCancellation;
private boolean mCancelled;
- private int mFlags;
- public PendingFillRequest(AssistStructure structure,
- Bundle extras, RemoteFillService service, int flags) {
- mStructure = structure;
- mExtras = extras;
- mFlags = flags;
+ public PendingFillRequest(FillRequest request, RemoteFillService service) {
+ mRequest = request;
mWeakService = new WeakReference<>(service);
mCallback = new IFillCallback.Stub() {
@Override
@@ -425,11 +420,11 @@
}
@Override
- public void onSuccess(FillResponse response) {
+ public void onSuccess(FillResponse response, int requestId) {
RemoteFillService remoteService = mWeakService.get();
if (remoteService != null) {
remoteService.dispatchOnFillRequestSuccess(
- PendingFillRequest.this, response);
+ PendingFillRequest.this, response, requestId);
}
}
@@ -449,8 +444,7 @@
RemoteFillService remoteService = mWeakService.get();
if (remoteService != null) {
try {
- remoteService.mAutoFillService.onFillRequest(mStructure,
- mExtras, mCallback, mFlags);
+ remoteService.mAutoFillService.onFillRequest(mRequest, mCallback);
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Error calling on fill request", e);
cancel();
@@ -481,14 +475,12 @@
private static final class PendingSaveRequest extends PendingRequest {
private final WeakReference<RemoteFillService> mWeakService;
- private final AssistStructure mStructure;
- private final Bundle mExtras;
+ private final SaveRequest mRequest;
private final ISaveCallback mCallback;
- public PendingSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle extras,
+ public PendingSaveRequest(@NonNull SaveRequest request,
@NonNull RemoteFillService service) {
- mStructure = structure;
- mExtras = extras;
+ mRequest = request;
mWeakService = new WeakReference<>(service);
mCallback = new ISaveCallback.Stub() {
@Override
@@ -516,7 +508,7 @@
final RemoteFillService service = mWeakService.get();
if (service != null) {
try {
- service.mAutoFillService.onSaveRequest(mStructure, mExtras, mCallback);
+ service.mAutoFillService.onSaveRequest(mRequest, mCallback);
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Error calling on save request", e);
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 1a31161..5feb81d 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -44,11 +44,15 @@
import android.os.RemoteException;
import android.service.autofill.AutofillService;
import android.service.autofill.Dataset;
+import android.service.autofill.FillContext;
+import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
import android.service.autofill.SaveInfo;
+import android.service.autofill.SaveRequest;
import android.util.ArrayMap;
import android.util.DebugUtils;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
@@ -125,7 +129,7 @@
RemoteFillService mRemoteFillService;
@GuardedBy("mLock")
- private ArrayList<FillResponse> mResponses;
+ private SparseArray<FillResponse> mResponses;
/**
* Response that requires a service authentitcation request.
@@ -156,7 +160,7 @@
* and used on subsequent {@code onFillRequest()} and {@code onSaveRequest()} calls.
*/
@GuardedBy("mLock")
- private Bundle mExtras;
+ private Bundle mClientState;
/**
* Flags used to start the session.
@@ -222,7 +226,7 @@
// FillServiceCallbacks
@Override
public void onFillRequestSuccess(@Nullable FillResponse response,
- @NonNull String servicePackageName) {
+ @NonNull String servicePackageName, int requestId) {
if (response == null) {
if ((mFlags & FLAG_MANUAL_REQUEST) != 0) {
getUiForShowing().showError(R.string.autofill_error_cannot_autofill);
@@ -243,7 +247,7 @@
// TODO(b/33197203 , b/35707731): make sure it's ignored if there is one already
mResponseWaitingAuth = response;
}
- processResponseLocked(response);
+ processResponseLocked(response, requestId);
}
final LogMaker log = (new LogMaker(MetricsEvent.AUTOFILL_REQUEST))
@@ -394,12 +398,18 @@
AutofillManager.EXTRA_AUTHENTICATION_RESULT);
if (result instanceof FillResponse) {
mMetricsLogger.action(MetricsEvent.AUTOFILL_AUTHENTICATED, mPackageName);
+ final int requestIndex = mResponses.indexOfValue(mResponseWaitingAuth);
mResponseWaitingAuth = null;
- processResponseLocked((FillResponse) result);
+ if (requestIndex >= 0) {
+ final int requestId = mResponses.keyAt(requestIndex);
+ processResponseLocked((FillResponse) result, requestId);
+ } else {
+ Slog.e(TAG, "Error cannot find id for auth response");
+ }
} else if (result instanceof Dataset) {
final Dataset dataset = (Dataset) result;
for (int i = 0; i < mResponses.size(); i++) {
- final FillResponse response = mResponses.get(i);
+ final FillResponse response = mResponses.valueAt(i);
final int index = response.getDatasets().indexOf(mDatasetWaitingAuth);
if (index >= 0) {
response.getDatasets().set(index, dataset);
@@ -440,12 +450,18 @@
return true;
}
- final FillResponse response = mResponses.get(mResponses.size() - 1);
+ final int lastResponseIdx = getLastResponseIndex();
+ if (lastResponseIdx < 0) {
+ Slog.d(TAG, "showSaveLocked(): mResponses=" + mResponses
+ + ", mViewStates=" + mViewStates);
+ return true;
+ }
+ final FillResponse response = mResponses.valueAt(lastResponseIdx);
final SaveInfo saveInfo = response.getSaveInfo();
if (DEBUG) {
- Slog.d(TAG,
- "showSaveLocked(): mResponses=" + mResponses + ", mViewStates=" + mViewStates);
+ Slog.d(TAG, "showSaveLocked(): mResponses=" + mResponses
+ + ", mViewStates=" + mViewStates);
}
/*
@@ -572,7 +588,15 @@
mStructure.dump();
}
- mRemoteFillService.onSaveRequest(mStructure, mExtras);
+ // TODO(b/33197203): Implement partitioning properly
+ final int lastResponseIdx = getLastResponseIndex();
+ final int requestId = mResponses.keyAt(lastResponseIdx);
+ final FillContext fillContext = new FillContext(requestId, mStructure);
+ final ArrayList fillContexts = new ArrayList(1);
+ fillContexts.add(fillContext);
+
+ final SaveRequest saveRequest = new SaveRequest(fillContexts, mClientState);
+ mRemoteFillService.onSaveRequest(saveRequest);
}
void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int flags) {
@@ -686,7 +710,9 @@
overlay.focused = id.equals(viewState.id);
node.setAutofillOverlay(overlay);
}
- mRemoteFillService.onFillRequest(mStructure, mExtras, 0);
+
+ FillRequest request = new FillRequest(mStructure, mClientState, 0);
+ mRemoteFillService.onFillRequest(request);
return newViewState;
}
@@ -723,17 +749,17 @@
}
}
- private void processResponseLocked(FillResponse response) {
+ private void processResponseLocked(FillResponse response, int requestId) {
if (DEBUG) {
Slog.d(TAG, "processResponseLocked(mCurrentViewId=" + mCurrentViewId + "):" + response);
}
if (mResponses == null) {
- mResponses = new ArrayList<>(4);
+ mResponses = new SparseArray<>(4);
}
- mResponses.add(response);
+ mResponses.put(requestId, response);
if (response != null) {
- mExtras = response.getExtras();
+ mClientState = response.getClientState();
}
setViewStatesLocked(response, ViewState.STATE_FILLABLE);
@@ -884,7 +910,8 @@
}
}
pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback);
- pw.print(prefix); pw.print("mExtras: "); pw.println(Helper.bundleToString(mExtras));
+ pw.print(prefix); pw.print("mClientState: "); pw.println(
+ Helper.bundleToString(mClientState));
mRemoteFillService.dump(prefix, pw);
}
@@ -961,4 +988,20 @@
destroyLocked();
mService.removeSessionLocked(id);
}
+
+ private int getLastResponseIndex() {
+ // The response ids are monotonically increasing so
+ // we just find the largest id which is the last. We
+ // do not rely on the internal ordering in sparse
+ // array to avoid - wow this stopped working!?
+ int lastResponseIdx = -1;
+ int lastResponseId = -1;
+ final int responseCount = mResponses.size();
+ for (int i = 0; i < responseCount; i++) {
+ if (mResponses.keyAt(i) > lastResponseId) {
+ lastResponseIdx = i;
+ }
+ }
+ return lastResponseIdx;
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 4449da9..ab6a3a7 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -161,7 +161,8 @@
log.setType(MetricsProto.MetricsEvent.TYPE_DETAIL);
hideFillUiUiThread();
if (mCallback != null) {
- mCallback.authenticate(response.getAuthentication(), response.getExtras());
+ mCallback.authenticate(response.getAuthentication(),
+ response.getClientState());
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 50c0a12..45b0384 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -55,6 +55,7 @@
import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
+import android.net.MatchAllNetworkSpecifier;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
@@ -4044,11 +4045,8 @@
throw new IllegalArgumentException("Bad timeout specified");
}
- if (NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER
- .equals(networkCapabilities.getNetworkSpecifier())) {
- throw new IllegalArgumentException("Invalid network specifier - must not be '"
- + NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER + "'");
- }
+ MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(
+ networkCapabilities.getNetworkSpecifier());
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
nextNetworkRequestId(), type);
@@ -4117,6 +4115,9 @@
enforceMeteredApnPolicy(networkCapabilities);
ensureRequestableCapabilities(networkCapabilities);
+ MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(
+ networkCapabilities.getNetworkSpecifier());
+
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation);
@@ -4178,6 +4179,9 @@
nc.addCapability(NET_CAPABILITY_FOREGROUND);
}
+ MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(
+ networkCapabilities.getNetworkSpecifier());
+
NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder);
@@ -4195,6 +4199,9 @@
enforceAccessPermission();
}
+ MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(
+ networkCapabilities.getNetworkSpecifier());
+
NetworkRequest networkRequest = new NetworkRequest(
new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c562cb9..7c7a3f8 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -42,6 +42,7 @@
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.MatchAllNetworkSpecifier;
import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
@@ -51,7 +52,9 @@
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkMisc;
import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
import android.net.RouteInfo;
+import android.net.StringNetworkSpecifier;
import android.net.metrics.IpConnectivityLog;
import android.net.util.MultinetworkPolicyTracker;
import android.os.ConditionVariable;
@@ -64,12 +67,15 @@
import android.os.MessageQueue;
import android.os.Messenger;
import android.os.MessageQueue.IdleHandler;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.Process;
import android.os.SystemClock;
import android.provider.Settings;
import android.test.AndroidTestCase;
import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
import android.util.Log;
import android.util.LogPrinter;
@@ -319,6 +325,11 @@
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
}
+ public void setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
+ mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
public void connectWithoutInternet() {
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
@@ -1819,34 +1830,130 @@
captivePortalCallback.assertNoCallback();
}
- @SmallTest
- public void testInvalidNetworkSpecifier() {
- boolean execptionCalled = true;
+ private NetworkRequest.Builder newWifiRequestBuilder() {
+ return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI);
+ }
- try {
- NetworkRequest.Builder builder = new NetworkRequest.Builder();
- builder.setNetworkSpecifier(MATCH_ALL_REQUESTS_NETWORK_SPECIFIER);
- execptionCalled = false;
- } catch (IllegalArgumentException e) {
- // do nothing - should get here
+ @SmallTest
+ public void testNetworkSpecifier() {
+ NetworkRequest rEmpty1 = newWifiRequestBuilder().build();
+ NetworkRequest rEmpty2 = newWifiRequestBuilder().setNetworkSpecifier((String) null).build();
+ NetworkRequest rEmpty3 = newWifiRequestBuilder().setNetworkSpecifier("").build();
+ NetworkRequest rEmpty4 = newWifiRequestBuilder().setNetworkSpecifier(
+ (NetworkSpecifier) null).build();
+ NetworkRequest rFoo = newWifiRequestBuilder().setNetworkSpecifier("foo").build();
+ NetworkRequest rBar = newWifiRequestBuilder().setNetworkSpecifier(
+ new StringNetworkSpecifier("bar")).build();
+
+ TestNetworkCallback cEmpty1 = new TestNetworkCallback();
+ TestNetworkCallback cEmpty2 = new TestNetworkCallback();
+ TestNetworkCallback cEmpty3 = new TestNetworkCallback();
+ TestNetworkCallback cEmpty4 = new TestNetworkCallback();
+ TestNetworkCallback cFoo = new TestNetworkCallback();
+ TestNetworkCallback cBar = new TestNetworkCallback();
+ TestNetworkCallback[] emptyCallbacks = new TestNetworkCallback[] {
+ cEmpty1, cEmpty2, cEmpty3 };
+
+ mCm.registerNetworkCallback(rEmpty1, cEmpty1);
+ mCm.registerNetworkCallback(rEmpty2, cEmpty2);
+ mCm.registerNetworkCallback(rEmpty3, cEmpty3);
+ mCm.registerNetworkCallback(rEmpty4, cEmpty4);
+ mCm.registerNetworkCallback(rFoo, cFoo);
+ mCm.registerNetworkCallback(rBar, cBar);
+
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false);
+ cEmpty1.expectAvailableCallbacks(mWiFiNetworkAgent);
+ cEmpty2.expectAvailableCallbacks(mWiFiNetworkAgent);
+ cEmpty3.expectAvailableCallbacks(mWiFiNetworkAgent);
+ cEmpty4.expectAvailableCallbacks(mWiFiNetworkAgent);
+ assertNoCallbacks(cFoo, cBar);
+
+ mWiFiNetworkAgent.setNetworkSpecifier(new StringNetworkSpecifier("foo"));
+ cFoo.expectAvailableCallbacks(mWiFiNetworkAgent);
+ for (TestNetworkCallback c: emptyCallbacks) {
+ c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
+ }
+ cFoo.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
+ cFoo.assertNoCallback();
+
+ mWiFiNetworkAgent.setNetworkSpecifier(new StringNetworkSpecifier("bar"));
+ cFoo.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ cBar.expectAvailableCallbacks(mWiFiNetworkAgent);
+ for (TestNetworkCallback c: emptyCallbacks) {
+ c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
+ }
+ cBar.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
+ cBar.assertNoCallback();
+
+ mWiFiNetworkAgent.setNetworkSpecifier(null);
+ cBar.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ for (TestNetworkCallback c: emptyCallbacks) {
+ c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
}
- assertTrue("NetworkRequest builder with MATCH_ALL_REQUESTS_NETWORK_SPECIFIER",
- execptionCalled);
+ assertNoCallbacks(cEmpty1, cEmpty2, cEmpty3, cFoo, cBar);
+ }
+
+ @SmallTest
+ public void testInvalidNetworkSpecifier() {
+ try {
+ NetworkRequest.Builder builder = new NetworkRequest.Builder();
+ builder.setNetworkSpecifier(new MatchAllNetworkSpecifier());
+ fail("NetworkRequest builder with MatchAllNetworkSpecifier");
+ } catch (IllegalArgumentException expected) {
+ // expected
+ }
try {
NetworkCapabilities networkCapabilities = new NetworkCapabilities();
networkCapabilities.addTransportType(TRANSPORT_WIFI)
- .setNetworkSpecifier(NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER);
+ .setNetworkSpecifier(new MatchAllNetworkSpecifier());
mService.requestNetwork(networkCapabilities, null, 0, null,
ConnectivityManager.TYPE_WIFI);
- execptionCalled = false;
- } catch (IllegalArgumentException e) {
- // do nothing - should get here
+ fail("ConnectivityService requestNetwork with MatchAllNetworkSpecifier");
+ } catch (IllegalArgumentException expected) {
+ // expected
}
- assertTrue("ConnectivityService requestNetwork with MATCH_ALL_REQUESTS_NETWORK_SPECIFIER",
- execptionCalled);
+ class NonParcelableSpecifier extends NetworkSpecifier {
+ public boolean satisfiedBy(NetworkSpecifier other) { return false; }
+ };
+ class ParcelableSpecifier extends NonParcelableSpecifier implements Parcelable {
+ @Override public int describeContents() { return 0; }
+ @Override public void writeToParcel(Parcel p, int flags) {}
+ }
+ NetworkRequest.Builder builder;
+
+ builder = new NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET);
+ try {
+ builder.setNetworkSpecifier(new NonParcelableSpecifier());
+ Parcel parcelW = Parcel.obtain();
+ builder.build().writeToParcel(parcelW, 0);
+ fail("Parceling a non-parcelable specifier did not throw an exception");
+ } catch (Exception e) {
+ // expected
+ }
+
+ builder = new NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET);
+ builder.setNetworkSpecifier(new ParcelableSpecifier());
+ NetworkRequest nr = builder.build();
+ assertNotNull(nr);
+
+ try {
+ Parcel parcelW = Parcel.obtain();
+ nr.writeToParcel(parcelW, 0);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+ NetworkRequest rereadNr = NetworkRequest.CREATOR.createFromParcel(parcelR);
+ fail("Unparceling a non-framework NetworkSpecifier did not throw an exception");
+ } catch (Exception e) {
+ // expected
+ }
}
@SmallTest