Merge changes from topics "201676043", "201676597", "204605351"
* changes:
Allow App Widget Overlays by Product Configuration.
Add Support for App Widgets as Overlays.
Centralized Dream Overlay State.
SystemUI IDreamOverlay Implementation.
Add Stub implementation for DreamOverlayService.
Consolidate dream overlay binding logic.
Pass Window to DreamOverlayService when attached.
Correct launch task behind when dream is present.
diff --git a/core/api/current.txt b/core/api/current.txt
index ec929ee..818e34c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9170,8 +9170,10 @@
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean requestMtu(int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic, boolean);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setPreferredPhy(int, int, int);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int writeCharacteristic(@NonNull android.bluetooth.BluetoothGattCharacteristic, @NonNull byte[], int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int writeDescriptor(@NonNull android.bluetooth.BluetoothGattDescriptor, @NonNull byte[]);
field public static final int CONNECTION_PRIORITY_BALANCED = 0; // 0x0
field public static final int CONNECTION_PRIORITY_HIGH = 1; // 0x1
field public static final int CONNECTION_PRIORITY_LOW_POWER = 2; // 0x2
@@ -9190,11 +9192,14 @@
public abstract class BluetoothGattCallback {
ctor public BluetoothGattCallback();
- method public void onCharacteristicChanged(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic);
- method public void onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
+ method @Deprecated public void onCharacteristicChanged(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic);
+ method public void onCharacteristicChanged(@NonNull android.bluetooth.BluetoothGatt, @NonNull android.bluetooth.BluetoothGattCharacteristic, @NonNull byte[]);
+ method @Deprecated public void onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
+ method public void onCharacteristicRead(@NonNull android.bluetooth.BluetoothGatt, @NonNull android.bluetooth.BluetoothGattCharacteristic, @NonNull byte[], int);
method public void onCharacteristicWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
method public void onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int);
- method public void onDescriptorRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
+ method @Deprecated public void onDescriptorRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
+ method public void onDescriptorRead(@NonNull android.bluetooth.BluetoothGatt, @NonNull android.bluetooth.BluetoothGattDescriptor, int, @NonNull byte[]);
method public void onDescriptorWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
method public void onMtuChanged(android.bluetooth.BluetoothGatt, int, int);
method public void onPhyRead(android.bluetooth.BluetoothGatt, int, int, int);
@@ -9211,20 +9216,20 @@
method public int describeContents();
method public android.bluetooth.BluetoothGattDescriptor getDescriptor(java.util.UUID);
method public java.util.List<android.bluetooth.BluetoothGattDescriptor> getDescriptors();
- method public Float getFloatValue(int, int);
+ method @Deprecated public Float getFloatValue(int, int);
method public int getInstanceId();
- method public Integer getIntValue(int, int);
+ method @Deprecated public Integer getIntValue(int, int);
method public int getPermissions();
method public int getProperties();
method public android.bluetooth.BluetoothGattService getService();
- method public String getStringValue(int);
+ method @Deprecated public String getStringValue(int);
method public java.util.UUID getUuid();
- method public byte[] getValue();
+ method @Deprecated public byte[] getValue();
method public int getWriteType();
- method public boolean setValue(byte[]);
- method public boolean setValue(int, int, int);
- method public boolean setValue(int, int, int, int);
- method public boolean setValue(String);
+ method @Deprecated public boolean setValue(byte[]);
+ method @Deprecated public boolean setValue(int, int, int);
+ method @Deprecated public boolean setValue(int, int, int, int);
+ method @Deprecated public boolean setValue(String);
method public void setWriteType(int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothGattCharacteristic> CREATOR;
@@ -9264,8 +9269,8 @@
method public android.bluetooth.BluetoothGattCharacteristic getCharacteristic();
method public int getPermissions();
method public java.util.UUID getUuid();
- method public byte[] getValue();
- method public boolean setValue(byte[]);
+ method @Deprecated public byte[] getValue();
+ method @Deprecated public boolean setValue(byte[]);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothGattDescriptor> CREATOR;
field public static final byte[] DISABLE_NOTIFICATION_VALUE;
@@ -9553,8 +9558,12 @@
field public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2; // 0x2
field public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1; // 0x1
field public static final int ERROR_DEVICE_NOT_BONDED = 3; // 0x3
- field public static final int ERROR_FEATURE_NOT_SUPPORTED = 8; // 0x8
+ field public static final int ERROR_FEATURE_NOT_SUPPORTED = 10; // 0xa
+ field public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 101; // 0x65
+ field public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 102; // 0x66
field public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6; // 0x6
+ field public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8; // 0x8
+ field public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9; // 0x9
field public static final int ERROR_UNKNOWN = 2147483647; // 0x7fffffff
field public static final int SUCCESS = 0; // 0x0
}
@@ -43648,8 +43657,10 @@
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiSettingEnabled();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVtSettingEnabled();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
+ method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback);
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterMmTelCapabilityCallback(@NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback);
field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1
field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0
@@ -43666,7 +43677,9 @@
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter();
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE", "android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE"}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
+ method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback);
field public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN = "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
}
@@ -43865,6 +43878,19 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsRegistrationAttributes> CREATOR;
}
+ public abstract class ImsStateCallback {
+ ctor public ImsStateCallback();
+ method public abstract void onAvailable();
+ method public abstract void onError();
+ method public abstract void onUnavailable(int);
+ field public static final int REASON_IMS_SERVICE_DISCONNECTED = 3; // 0x3
+ field public static final int REASON_IMS_SERVICE_NOT_READY = 6; // 0x6
+ field public static final int REASON_NO_IMS_SERVICE_CONFIGURED = 4; // 0x4
+ field public static final int REASON_SUBSCRIPTION_INACTIVE = 5; // 0x5
+ field public static final int REASON_UNKNOWN_PERMANENT_ERROR = 2; // 0x2
+ field public static final int REASON_UNKNOWN_TEMPORARY_ERROR = 1; // 0x1
+ }
+
public class RcsUceAdapter {
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index b97fd5c..3046061 100755
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -13953,7 +13953,9 @@
method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int);
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public boolean isSupported() throws android.telephony.ims.ImsException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void triggerFullNetworkRegistration(@NonNull android.telephony.ims.SipDelegateConnection, @IntRange(from=100, to=699) int, @Nullable String);
+ method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback);
field public static final int DENIED_REASON_INVALID = 4; // 0x4
field public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; // 0x1
field public static final int DENIED_REASON_NOT_ALLOWED = 2; // 0x2
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 65cdc83..4e7c01a 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
@@ -29,6 +31,8 @@
import android.os.RemoteException;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@@ -140,27 +144,6 @@
public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
/**
- * A GATT writeCharacteristic request is started successfully.
- *
- * @hide
- */
- public static final int GATT_WRITE_REQUEST_SUCCESS = 0;
-
- /**
- * A GATT writeCharacteristic request failed to start.
- *
- * @hide
- */
- public static final int GATT_WRITE_REQUEST_FAIL = 1;
-
- /**
- * A GATT writeCharacteristic request is issued to a busy remote device.
- *
- * @hide
- */
- public static final int GATT_WRITE_REQUEST_BUSY = 2;
-
- /**
* No authentication required.
*
* @hide
@@ -430,6 +413,9 @@
if (callback != null) {
if (status == 0) characteristic.setValue(value);
callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
+ value, status);
+ // Keep calling deprecated callback to maintain app compatibility
+ callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
status);
}
}
@@ -443,7 +429,8 @@
*/
@Override
@SuppressLint("AndroidFrameworkRequiresPermission")
- public void onCharacteristicWrite(String address, int status, int handle) {
+ public void onCharacteristicWrite(String address, int status, int handle,
+ byte[] value) {
if (VDBG) {
Log.d(TAG, "onCharacteristicWrite() - Device=" + address
+ " handle=" + handle + " Status=" + status);
@@ -467,12 +454,13 @@
try {
final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
- int requestStatus = GATT_WRITE_REQUEST_FAIL;
+ int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN;
for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
- requestStatus = mService.writeCharacteristic(mClientIf, address, handle,
- characteristic.getWriteType(), authReq,
- characteristic.getValue(), mAttributionSource);
- if (requestStatus != GATT_WRITE_REQUEST_BUSY) {
+ requestStatus = mService.writeCharacteristic(mClientIf, address,
+ handle, characteristic.getWriteType(), authReq,
+ value, mAttributionSource);
+ if (requestStatus
+ != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) {
break;
}
try {
@@ -488,7 +476,6 @@
}
mAuthRetryState = AUTH_RETRY_STATE_IDLE;
-
runOrQueueCallback(new Runnable() {
@Override
public void run() {
@@ -525,6 +512,9 @@
if (callback != null) {
characteristic.setValue(value);
callback.onCharacteristicChanged(BluetoothGatt.this,
+ characteristic, value);
+ // Keep calling deprecated callback to maintain app compatibility
+ callback.onCharacteristicChanged(BluetoothGatt.this,
characteristic);
}
}
@@ -578,6 +568,9 @@
final BluetoothGattCallback callback = mCallback;
if (callback != null) {
if (status == 0) descriptor.setValue(value);
+ callback.onDescriptorRead(BluetoothGatt.this, descriptor, status,
+ value);
+ // Keep calling deprecated callback to maintain app compatibility
callback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
}
}
@@ -590,7 +583,8 @@
*/
@Override
@SuppressLint("AndroidFrameworkRequiresPermission")
- public void onDescriptorWrite(String address, int status, int handle) {
+ public void onDescriptorWrite(String address, int status, int handle,
+ byte[] value) {
if (VDBG) {
Log.d(TAG,
"onDescriptorWrite() - Device=" + address + " handle=" + handle);
@@ -614,7 +608,7 @@
final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
mService.writeDescriptor(mClientIf, address, handle,
- authReq, descriptor.getValue(), mAttributionSource);
+ authReq, value, mAttributionSource);
mAuthRetryState++;
return;
} catch (RemoteException e) {
@@ -1194,8 +1188,8 @@
* Reads the requested characteristic from the associated remote device.
*
* <p>This is an asynchronous operation. The result of the read operation
- * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
- * callback.
+ * is reported by the {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt,
+ * BluetoothGattCharacteristic, byte[], int)} callback.
*
* @param characteristic Characteristic to read from the remote device
* @return true, if the read operation was initiated successfully
@@ -1240,8 +1234,8 @@
* Reads the characteristic using its UUID from the associated remote device.
*
* <p>This is an asynchronous operation. The result of the read operation
- * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
- * callback.
+ * is reported by the {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt,
+ * BluetoothGattCharacteristic, byte[], int)} callback.
*
* @param uuid UUID of characteristic to read from the remote device
* @return true, if the read operation was initiated successfully
@@ -1284,40 +1278,94 @@
*
* @param characteristic Characteristic to write on the remote device
* @return true, if the write operation was initiated successfully
+ * @throws IllegalArgumentException if characteristic or its value are null
+ *
+ * @deprecated Use {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[],
+ * int)} as this is not memory safe.
*/
+ @Deprecated
@RequiresLegacyBluetoothPermission
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
+ try {
+ return writeCharacteristic(characteristic, characteristic.getValue(),
+ characteristic.getWriteType()) == BluetoothStatusCodes.SUCCESS;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ BluetoothStatusCodes.SUCCESS,
+ BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
+ BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION,
+ BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED,
+ BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND,
+ BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED,
+ BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY,
+ BluetoothStatusCodes.ERROR_UNKNOWN
+ })
+ public @interface WriteOperationReturnValues{}
+
+ /**
+ * Writes a given characteristic and its values to the associated remote device.
+ *
+ * <p>Once the write operation has been completed, the
+ * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
+ * reporting the result of the operation.
+ *
+ * @param characteristic Characteristic to write on the remote device
+ * @return whether the characteristic was successfully written to
+ * @throws IllegalArgumentException if characteristic or value are null
+ */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @WriteOperationReturnValues
+ public int writeCharacteristic(@NonNull BluetoothGattCharacteristic characteristic,
+ @NonNull byte[] value, int writeType) {
+ if (characteristic == null) {
+ throw new IllegalArgumentException("characteristic must not be null");
+ }
+ if (value == null) {
+ throw new IllegalArgumentException("value must not be null");
+ }
+ if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
&& (characteristic.getProperties()
& BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) {
- return false;
+ return BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED;
+ }
+ if (mService == null || mClientIf == 0) {
+ return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
}
- if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
- if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
-
BluetoothGattService service = characteristic.getService();
- if (service == null) return false;
+ if (service == null) {
+ throw new IllegalArgumentException("Characteristic must have a non-null service");
+ }
BluetoothDevice device = service.getDevice();
- if (device == null) return false;
+ if (device == null) {
+ throw new IllegalArgumentException("Service must have a non-null device");
+ }
synchronized (mDeviceBusyLock) {
if (mDeviceBusy) {
- return false;
+ return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY;
}
mDeviceBusy = true;
}
- int requestStatus = GATT_WRITE_REQUEST_FAIL;
+ int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN;
try {
for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
requestStatus = mService.writeCharacteristic(mClientIf, device.getAddress(),
- characteristic.getInstanceId(), characteristic.getWriteType(),
- AUTHENTICATION_NONE, characteristic.getValue(), mAttributionSource);
- if (requestStatus != GATT_WRITE_REQUEST_BUSY) {
+ characteristic.getInstanceId(), writeType, AUTHENTICATION_NONE, value,
+ mAttributionSource);
+ if (requestStatus != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) {
break;
}
try {
@@ -1330,10 +1378,10 @@
synchronized (mDeviceBusyLock) {
mDeviceBusy = false;
}
- return false;
+ throw e.rethrowFromSystemServer();
}
- return requestStatus == GATT_WRITE_REQUEST_SUCCESS;
+ return requestStatus;
}
/**
@@ -1384,45 +1432,86 @@
/**
* Write the value of a given descriptor to the associated remote device.
*
- * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
- * triggered to report the result of the write operation.
+ * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the
+ * result of the write operation.
*
* @param descriptor Descriptor to write to the associated remote device
* @return true, if the write operation was initiated successfully
+ * @throws IllegalArgumentException if descriptor or its value are null
+ *
+ * @deprecated Use {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])} as
+ * this is not memory safe.
*/
+ @Deprecated
@RequiresLegacyBluetoothPermission
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
+ try {
+ return writeDescriptor(descriptor, descriptor.getValue())
+ == BluetoothStatusCodes.SUCCESS;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /**
+ * Write the value of a given descriptor to the associated remote device.
+ *
+ * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the
+ * result of the write operation.
+ *
+ * @param descriptor Descriptor to write to the associated remote device
+ * @return true, if the write operation was initiated successfully
+ * @throws IllegalArgumentException if descriptor or value are null
+ */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @WriteOperationReturnValues
+ public int writeDescriptor(@NonNull BluetoothGattDescriptor descriptor,
+ @NonNull byte[] value) {
+ if (descriptor == null) {
+ throw new IllegalArgumentException("descriptor must not be null");
+ }
+ if (value == null) {
+ throw new IllegalArgumentException("value must not be null");
+ }
if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
- if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
+ if (mService == null || mClientIf == 0) {
+ return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
+ }
BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
- if (characteristic == null) return false;
+ if (characteristic == null) {
+ throw new IllegalArgumentException("Descriptor must have a non-null characteristic");
+ }
BluetoothGattService service = characteristic.getService();
- if (service == null) return false;
+ if (service == null) {
+ throw new IllegalArgumentException("Characteristic must have a non-null service");
+ }
BluetoothDevice device = service.getDevice();
- if (device == null) return false;
+ if (device == null) {
+ throw new IllegalArgumentException("Service must have a non-null device");
+ }
synchronized (mDeviceBusyLock) {
- if (mDeviceBusy) return false;
+ if (mDeviceBusy) return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY;
mDeviceBusy = true;
}
try {
- mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
- AUTHENTICATION_NONE, descriptor.getValue(), mAttributionSource);
+ return mService.writeDescriptor(mClientIf, device.getAddress(),
+ descriptor.getInstanceId(), AUTHENTICATION_NONE, value, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
synchronized (mDeviceBusyLock) {
mDeviceBusy = false;
}
- return false;
+ e.rethrowFromSystemServer();
}
-
- return true;
+ return BluetoothStatusCodes.ERROR_UNKNOWN;
}
/**
@@ -1431,9 +1520,9 @@
* <p>Once a reliable write transaction has been initiated, all calls
* to {@link #writeCharacteristic} are sent to the remote device for
* verification and queued up for atomic execution. The application will
- * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
- * in response to every {@link #writeCharacteristic} call and is responsible
- * for verifying if the value has been transmitted accurately.
+ * receive a {@link BluetoothGattCallback#onCharacteristicWrite} callback in response to every
+ * {@link #writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} call and is
+ * responsible for verifying if the value has been transmitted accurately.
*
* <p>After all characteristics have been queued up and verified,
* {@link #executeReliableWrite} will execute all writes. If a characteristic
@@ -1530,9 +1619,9 @@
* Enable or disable notifications/indications for a given characteristic.
*
* <p>Once notifications are enabled for a characteristic, a
- * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
- * triggered if the remote device indicates that the given characteristic
- * has changed.
+ * {@link BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt,
+ * BluetoothGattCharacteristic, byte[])} callback will be triggered if the remote device
+ * indicates that the given characteristic has changed.
*
* @param characteristic The characteristic for which to enable notifications
* @param enable Set to true to enable notifications/indications
diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java
index 1c40cff..d0a5a1e 100644
--- a/core/java/android/bluetooth/BluetoothGattCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattCallback.java
@@ -80,16 +80,34 @@
/**
* Callback reporting the result of a characteristic read operation.
*
- * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic}
+ * @param gatt GATT client invoked
+ * {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)}
* @param characteristic Characteristic that was read from the associated remote device.
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
- * successfully.
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
+ * successfully.
+ * @deprecated Use {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt,
+ * BluetoothGattCharacteristic, byte[], int)} as it is memory safe
*/
+ @Deprecated
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,
int status) {
}
/**
+ * Callback reporting the result of a characteristic read operation.
+ *
+ * @param gatt GATT client invoked
+ * {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)}
+ * @param characteristic Characteristic that was read from the associated remote device.
+ * @param value the value of the characteristic
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
+ * successfully.
+ */
+ public void onCharacteristicRead(@NonNull BluetoothGatt gatt, @NonNull
+ BluetoothGattCharacteristic characteristic, @NonNull byte[] value, int status) {
+ }
+
+ /**
* Callback indicating the result of a characteristic write operation.
*
* <p>If this callback is invoked while a reliable write transaction is
@@ -98,10 +116,13 @@
* value to the desired value to be written. If the values don't match,
* the application must abort the reliable write transaction.
*
- * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic}
+ * @param gatt GATT client that invoked
+ * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic,
+ * byte[], int)}
* @param characteristic Characteristic that was written to the associated remote device.
- * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the
- * operation succeeds.
+ * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if
+ * the
+ * operation succeeds.
*/
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
@@ -110,33 +131,68 @@
/**
* Callback triggered as a result of a remote characteristic notification.
*
- * @param gatt GATT client the characteristic is associated with
+ * @param gatt GATT client the characteristic is associated with
* @param characteristic Characteristic that has been updated as a result of a remote
- * notification event.
+ * notification event.
+ * @deprecated Use {@link BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt,
+ * BluetoothGattCharacteristic, byte[])} as it is memory safe by providing the characteristic
+ * value at the time of notification.
*/
+ @Deprecated
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
}
/**
+ * Callback triggered as a result of a remote characteristic notification. Note that the value
+ * within the characteristic object may have changed since receiving the remote characteristic
+ * notification, so check the parameter value for the value at the time of notification.
+ *
+ * @param gatt GATT client the characteristic is associated with
+ * @param characteristic Characteristic that has been updated as a result of a remote
+ * notification event.
+ * @param value notified characteristic value
+ */
+ public void onCharacteristicChanged(@NonNull BluetoothGatt gatt,
+ @NonNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value) {
+ }
+
+ /**
* Callback reporting the result of a descriptor read operation.
*
- * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor}
+ * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor}
* @param descriptor Descriptor that was read from the associated remote device.
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
- * successfully
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
+ * successfully
+ * @deprecated Use {@link BluetoothGattCallback#onDescriptorRead(BluetoothGatt,
+ * BluetoothGattDescriptor, int, byte[])} as it is memory safe by providing the descriptor
+ * value at the time it was read.
*/
+ @Deprecated
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
int status) {
}
/**
+ * Callback reporting the result of a descriptor read operation.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor}
+ * @param descriptor Descriptor that was read from the associated remote device.
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
+ * successfully
+ * @param value the descriptor value at the time of the read operation
+ */
+ public void onDescriptorRead(@NonNull BluetoothGatt gatt,
+ @NonNull BluetoothGattDescriptor descriptor, int status, @NonNull byte[] value) {
+ }
+
+ /**
* Callback indicating the result of a descriptor write operation.
*
- * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor}
+ * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor}
* @param descriptor Descriptor that was writte to the associated remote device.
- * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the
- * operation succeeds.
+ * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the
+ * operation succeeds.
*/
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
int status) {
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
index 8a7d4ba..c5e986e 100644
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -451,8 +451,8 @@
* Set the write type for this characteristic
*
* <p>Setting the write type of a characteristic determines how the
- * {@link BluetoothGatt#writeCharacteristic} function write this
- * characteristic.
+ * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} function
+ * write this characteristic.
*
* @param writeType The write type to for this characteristic. Can be one of: {@link
* #WRITE_TYPE_DEFAULT}, {@link #WRITE_TYPE_NO_RESPONSE} or {@link #WRITE_TYPE_SIGNED}.
@@ -504,7 +504,10 @@
* operation or if a characteristic update notification has been received.
*
* @return Cached value of the characteristic
+ *
+ * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} instead
*/
+ @Deprecated
public byte[] getValue() {
return mValue;
}
@@ -521,7 +524,11 @@
* @param formatType The format type used to interpret the characteristic value.
* @param offset Offset at which the integer value can be found.
* @return Cached value of the characteristic or null of offset exceeds value size.
+ *
+ * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get
+ * the characteristic value
*/
+ @Deprecated
public Integer getIntValue(int formatType, int offset) {
if ((offset + getTypeLen(formatType)) > mValue.length) return null;
@@ -558,7 +565,11 @@
* @param offset Offset at which the float value can be found.
* @return Cached value of the characteristic at a given offset or null if the requested offset
* exceeds the value size.
+ *
+ * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get
+ * the characteristic value
*/
+ @Deprecated
public Float getFloatValue(int formatType, int offset) {
if ((offset + getTypeLen(formatType)) > mValue.length) return null;
@@ -580,7 +591,11 @@
*
* @param offset Offset at which the string value can be found.
* @return Cached value of the characteristic
+ *
+ * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get
+ * the characteristic value
*/
+ @Deprecated
public String getStringValue(int offset) {
if (mValue == null || offset > mValue.length) return null;
byte[] strBytes = new byte[mValue.length - offset];
@@ -599,7 +614,11 @@
* @param value New value for this characteristic
* @return true if the locally stored value has been set, false if the requested value could not
* be stored locally.
+ *
+ * @deprecated Pass the characteristic value directly into
+ * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
*/
+ @Deprecated
public boolean setValue(byte[] value) {
mValue = value;
return true;
@@ -613,7 +632,11 @@
* @param formatType Integer format type used to transform the value parameter
* @param offset Offset at which the value should be placed
* @return true if the locally stored value has been set
+ *
+ * @deprecated Pass the characteristic value directly into
+ * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
*/
+ @Deprecated
public boolean setValue(int value, int formatType, int offset) {
int len = offset + getTypeLen(formatType);
if (mValue == null) mValue = new byte[len];
@@ -660,7 +683,11 @@
* @param formatType Float format type used to transform the value parameter
* @param offset Offset at which the value should be placed
* @return true if the locally stored value has been set
+ *
+ * @deprecated Pass the characteristic value directly into
+ * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
*/
+ @Deprecated
public boolean setValue(int mantissa, int exponent, int formatType, int offset) {
int len = offset + getTypeLen(formatType);
if (mValue == null) mValue = new byte[len];
@@ -697,7 +724,11 @@
*
* @param value New value for this characteristic
* @return true if the locally stored value has been set
+ *
+ * @deprecated Pass the characteristic value directly into
+ * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
*/
+ @Deprecated
public boolean setValue(String value) {
mValue = value.getBytes();
return true;
diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java
index ed5ea08..a35d5b9 100644
--- a/core/java/android/bluetooth/BluetoothGattDescriptor.java
+++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java
@@ -260,7 +260,10 @@
* operation.
*
* @return Cached value of the descriptor
+ *
+ * @deprecated Use {@link BluetoothGatt#readDescriptor(BluetoothGattDescriptor)} instead
*/
+ @Deprecated
public byte[] getValue() {
return mValue;
}
@@ -276,7 +279,11 @@
* @param value New value for this descriptor
* @return true if the locally stored value has been set, false if the requested value could not
* be stored locally.
+ *
+ * @deprecated Pass the descriptor value directly into
+ * {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])}
*/
+ @Deprecated
public boolean setValue(byte[] value) {
mValue = value;
return true;
diff --git a/core/java/android/bluetooth/BluetoothStatusCodes.java b/core/java/android/bluetooth/BluetoothStatusCodes.java
index 63e84ed..ca01784 100644
--- a/core/java/android/bluetooth/BluetoothStatusCodes.java
+++ b/core/java/android/bluetooth/BluetoothStatusCodes.java
@@ -79,9 +79,31 @@
public static final int ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION = 7;
/**
+ * Error code indicating that the caller does not have the
+ * {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission
+ */
+ public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8;
+
+ /**
+ * Error code indicating that the profile service is not bound. You can bind a profile service
+ * by calling {@link BluetoothAdapter#getProfileProxy}
+ */
+ public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9;
+
+ /**
* Error code indicating that the feature is not supported.
*/
- public static final int ERROR_FEATURE_NOT_SUPPORTED = 8;
+ public static final int ERROR_FEATURE_NOT_SUPPORTED = 10;
+
+ /**
+ * A GATT writeCharacteristic request is not permitted on the remote device.
+ */
+ public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 101;
+
+ /**
+ * A GATT writeCharacteristic request is issued to a busy remote device.
+ */
+ public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 102;
/**
* If another application has already requested {@link OobData} then another fetch will be
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 2c89a15..7e2792c 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -79,7 +79,7 @@
DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "true");
DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "true");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true");
- DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "false");
+ DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "true");
DEFAULT_FLAGS.put(SETTINGS_APP_LANGUAGE_SELECTION, "false");
}
diff --git a/core/java/android/view/TaskTransitionSpec.java b/core/java/android/view/TaskTransitionSpec.java
index e90d6e1..5f498a1 100644
--- a/core/java/android/view/TaskTransitionSpec.java
+++ b/core/java/android/view/TaskTransitionSpec.java
@@ -39,6 +39,7 @@
/**
* TEMPORARY FIELD (b/202383002)
* TODO: Remove once we use surfaceflinger rounded corners on tasks rather than taskbar overlays
+ * or when shell transitions are fully enabled
*
* A set of {@InsetsState.InternalInsetsType}s we want to use as the source to set the bounds
* of the task during the animation. Used to make sure that task animate above the taskbar.
diff --git a/core/java/android/window/SplashScreen.java b/core/java/android/window/SplashScreen.java
index 3354a6c..b9bf009 100644
--- a/core/java/android/window/SplashScreen.java
+++ b/core/java/android/window/SplashScreen.java
@@ -92,6 +92,9 @@
* overrides and persists the theme used for the {@link SplashScreen} of this application.
* <p>
* To reset to the default theme, set this the themeId to {@link Resources#ID_NULL}.
+ * <p>
+ * <b>Note:</b> The theme name must be stable across versions, otherwise it won't be found
+ * after your application is updated.
*/
void setSplashScreenTheme(@StyleRes int themeId);
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 06333e1..819857f 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8735,9 +8735,14 @@
<declare-styleable name="VoiceInteractionService">
<!-- The service that hosts active voice interaction sessions. This is required. -->
<attr name="sessionService" format="string" />
- <!-- The service that provides voice recognition. This is required. When the user
- selects this voice interaction service, they will also be implicitly selecting
- the component here for their recognition service. -->
+ <!-- The service that provides voice recognition. This is required. On Android 11 and
+ earlier, this must be a valid RecognitionService.
+ <p>
+ From Android 12 onward, this attribute does nothing. However, we still require it to
+ be set to something to reduce the risk that an app with an unspecified value gets
+ pushed to older platform versions, where it will cause a boot loop. To make sure
+ developers don't miss it, the system will reset the current assistant if this isn't
+ specified.-->
<attr name="recognitionService" format="string" />
<attr name="settingsActivity" />
<!-- Flag indicating whether this voice interaction service is capable of handling the
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java
index e6ad011..eb94297 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java
@@ -30,18 +30,21 @@
/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
final class CommonDisplayFeature implements DisplayFeature {
private static final Pattern FEATURE_PATTERN =
- Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]");
+ Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]-?(flat|half-opened)?");
private static final String FEATURE_TYPE_FOLD = "fold";
private static final String FEATURE_TYPE_HINGE = "hinge";
+ private static final String PATTERN_STATE_FLAT = "flat";
+ private static final String PATTERN_STATE_HALF_OPENED = "half-opened";
+
// TODO(b/183049815): Support feature strings that include the state of the feature.
+
/**
* Parses a display feature from a string.
*
* @throws IllegalArgumentException if the provided string is improperly formatted or could not
- * otherwise be parsed.
- *
+ * otherwise be parsed.
* @see #FEATURE_PATTERN
*/
@NonNull
@@ -52,6 +55,7 @@
}
try {
String featureType = featureMatcher.group(1);
+ featureType = featureType == null ? "" : featureType;
int type;
switch (featureType) {
case FEATURE_TYPE_FOLD:
@@ -73,8 +77,21 @@
if (isZero(featureRect)) {
throw new IllegalArgumentException("Feature has empty bounds: " + string);
}
-
- return new CommonDisplayFeature(type, null, featureRect);
+ String stateString = featureMatcher.group(6);
+ stateString = stateString == null ? "" : stateString;
+ Integer state;
+ switch (stateString) {
+ case PATTERN_STATE_FLAT:
+ state = COMMON_STATE_FLAT;
+ break;
+ case PATTERN_STATE_HALF_OPENED:
+ state = COMMON_STATE_HALF_OPENED;
+ break;
+ default:
+ state = null;
+ break;
+ }
+ return new CommonDisplayFeature(type, state, featureRect);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Malformed feature description: " + string, e);
}
@@ -87,6 +104,7 @@
private final Rect mRect;
CommonDisplayFeature(int type, @Nullable Integer state, @NonNull Rect rect) {
+ assertValidState(state);
this.mType = type;
this.mState = state;
if (rect.width() == 0 && rect.height() == 0) {
@@ -125,4 +143,11 @@
public int hashCode() {
return Objects.hash(mType, mState, mRect);
}
+
+ private static void assertValidState(@Nullable Integer state) {
+ if (state != null && state != COMMON_STATE_FLAT && state != COMMON_STATE_HALF_OPENED) {
+ throw new IllegalArgumentException("Invalid state: " + state
+ + "must be either COMMON_STATE_FLAT or COMMON_STATE_HALF_OPENED");
+ }
+ }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java
index b6c4c43..5736418 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java
@@ -16,11 +16,15 @@
package androidx.window.common;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Rect;
import androidx.annotation.NonNull;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
public interface DisplayFeature {
/** Returns the type of the feature. */
@@ -28,9 +32,29 @@
/** Returns the state of the feature, or {@code null} if the feature has no state. */
@Nullable
+ @State
Integer getState();
/** Returns the bounds of the feature. */
@NonNull
Rect getRect();
+
+ /**
+ * A common state to represent a FLAT hinge. This is needed because the definitions in Sidecar
+ * and Extensions do not match exactly.
+ */
+ int COMMON_STATE_FLAT = 3;
+ /**
+ * A common state to represent a HALF_OPENED hinge. This is needed because the definitions in
+ * Sidecar and Extensions do not match exactly.
+ */
+ int COMMON_STATE_HALF_OPENED = 2;
+
+ /**
+ * The possible states for a folding hinge.
+ */
+ @IntDef({COMMON_STATE_FLAT, COMMON_STATE_HALF_OPENED})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface State {}
+
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index 383d91d..32d447e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -122,8 +122,19 @@
private int getFeatureState(DisplayFeature feature) {
Integer featureState = feature.getState();
Optional<Integer> posture = mDevicePostureProducer.getData();
- int fallbackPosture = posture.orElse(FoldingFeature.STATE_FLAT);
- return featureState == null ? fallbackPosture : featureState;
+ int fallbackPosture = posture.orElse(DisplayFeature.COMMON_STATE_FLAT);
+ int displayFeatureState = featureState == null ? fallbackPosture : featureState;
+ return convertToExtensionState(displayFeatureState);
+ }
+
+ private int convertToExtensionState(int state) {
+ switch (state) {
+ case DisplayFeature.COMMON_STATE_FLAT:
+ return FoldingFeature.STATE_FLAT;
+ case DisplayFeature.COMMON_STATE_HALF_OPENED:
+ return FoldingFeature.STATE_HALF_OPENED;
+ }
+ return FoldingFeature.STATE_FLAT;
}
private void onDisplayFeaturesChanged() {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
index ece198c..aa949f1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
@@ -38,6 +38,7 @@
import androidx.window.util.PriorityDataProducer;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Optional;
@@ -47,6 +48,7 @@
*/
class SampleSidecarImpl extends StubSidecar {
private static final String TAG = "SampleSidecar";
+ private static final boolean DEBUG = false;
private final SettingsDevicePostureProducer mSettingsDevicePostureProducer;
private final DataProducer<Integer> mDevicePostureProducer;
@@ -88,10 +90,30 @@
Optional<Integer> posture = mDevicePostureProducer.getData();
SidecarDeviceState deviceState = new SidecarDeviceState();
- deviceState.posture = posture.orElse(SidecarDeviceState.POSTURE_UNKNOWN);
+ deviceState.posture = posture.orElse(deviceStateFromFeature());
return deviceState;
}
+ private int deviceStateFromFeature() {
+ List<DisplayFeature> storedFeatures = mDisplayFeatureProducer.getData()
+ .orElse(Collections.emptyList());
+ for (int i = 0; i < storedFeatures.size(); i++) {
+ DisplayFeature feature = storedFeatures.get(i);
+ final int state = feature.getState() == null ? -1 : feature.getState();
+ if (DEBUG && feature.getState() == null) {
+ Log.d(TAG, "feature#getState was null for DisplayFeature: " + feature);
+ }
+
+ switch (state) {
+ case DisplayFeature.COMMON_STATE_FLAT:
+ return SidecarDeviceState.POSTURE_OPENED;
+ case DisplayFeature.COMMON_STATE_HALF_OPENED:
+ return SidecarDeviceState.POSTURE_HALF_OPENED;
+ }
+ }
+ return SidecarDeviceState.POSTURE_UNKNOWN;
+ }
+
@NonNull
@Override
public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
index ab3cbd6..04d974a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
@@ -24,11 +24,16 @@
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -42,7 +47,7 @@
* Controls to show/update restart-activity buttons on Tasks based on whether the foreground
* activities are in size compatibility mode.
*/
-public class SizeCompatUIController implements DisplayController.OnDisplaysChangedListener,
+public class SizeCompatUIController implements OnDisplaysChangedListener,
DisplayImeController.ImePositionProcessor {
/** Callback for size compat UI interaction. */
@@ -58,6 +63,10 @@
/** Whether the IME is shown on display id. */
private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1);
+ /** {@link PerDisplayOnInsetsChangedListener} by display id. */
+ private final SparseArray<PerDisplayOnInsetsChangedListener> mOnInsetsChangedListeners =
+ new SparseArray<>(0);
+
/** The showing UIs by task id. */
private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0);
@@ -66,6 +75,7 @@
private final Context mContext;
private final DisplayController mDisplayController;
+ private final DisplayInsetsController mDisplayInsetsController;
private final DisplayImeController mImeController;
private final SyncTransactionQueue mSyncQueue;
@@ -76,10 +86,12 @@
public SizeCompatUIController(Context context,
DisplayController displayController,
+ DisplayInsetsController displayInsetsController,
DisplayImeController imeController,
SyncTransactionQueue syncQueue) {
mContext = context;
mDisplayController = displayController;
+ mDisplayInsetsController = displayInsetsController;
mImeController = imeController;
mSyncQueue = syncQueue;
mDisplayController.addDisplayWindowListener(this);
@@ -115,8 +127,14 @@
}
@Override
+ public void onDisplayAdded(int displayId) {
+ addOnInsetsChangedListener(displayId);
+ }
+
+ @Override
public void onDisplayRemoved(int displayId) {
mDisplayContextCache.remove(displayId);
+ removeOnInsetsChangedListener(displayId);
// Remove all size compat UIs on the removed display.
final List<Integer> toRemoveTaskIds = new ArrayList<>();
@@ -126,8 +144,29 @@
}
}
+ private void addOnInsetsChangedListener(int displayId) {
+ PerDisplayOnInsetsChangedListener listener = new PerDisplayOnInsetsChangedListener(
+ displayId);
+ listener.register();
+ mOnInsetsChangedListeners.put(displayId, listener);
+ }
+
+ private void removeOnInsetsChangedListener(int displayId) {
+ PerDisplayOnInsetsChangedListener listener = mOnInsetsChangedListeners.get(displayId);
+ if (listener == null) {
+ return;
+ }
+ listener.unregister();
+ mOnInsetsChangedListeners.remove(displayId);
+ }
+
+
@Override
public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ updateDisplayLayout(displayId);
+ }
+
+ private void updateDisplayLayout(int displayId) {
final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(displayId);
forAllLayoutsOnDisplay(displayId, layout -> layout.updateDisplayLayout(displayLayout));
}
@@ -219,4 +258,37 @@
}
}
}
+
+ /** An implementation of {@link OnInsetsChangedListener} for a given display id. */
+ private class PerDisplayOnInsetsChangedListener implements OnInsetsChangedListener {
+ final int mDisplayId;
+ final InsetsState mInsetsState = new InsetsState();
+
+ PerDisplayOnInsetsChangedListener(int displayId) {
+ mDisplayId = displayId;
+ }
+
+ void register() {
+ mDisplayInsetsController.addInsetsChangedListener(mDisplayId, this);
+ }
+
+ void unregister() {
+ mDisplayInsetsController.removeInsetsChangedListener(mDisplayId, this);
+ }
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ if (mInsetsState.equals(insetsState)) {
+ return;
+ }
+ mInsetsState.set(insetsState);
+ updateDisplayLayout(mDisplayId);
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) {
+ insetsChanged(insetsState);
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
index bebb6d3..1a2c94f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
@@ -55,6 +55,7 @@
private final int mTaskId;
private ShellTaskOrganizer.TaskListener mTaskListener;
private DisplayLayout mDisplayLayout;
+ private final Rect mStableBounds;
private final int mButtonWidth;
private final int mButtonHeight;
private final int mPopupOffsetX;
@@ -89,6 +90,9 @@
mShouldShowHint = !hasShownHint;
mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this);
+ mStableBounds = new Rect();
+ mDisplayLayout.getStableBounds(mStableBounds);
+
final Resources resources = mContext.getResources();
mButtonWidth = resources.getDimensionPixelSize(R.dimen.size_compat_button_width);
mButtonHeight = resources.getDimensionPixelSize(R.dimen.size_compat_button_height);
@@ -173,8 +177,7 @@
if (!taskConfig.windowConfiguration.getBounds()
.equals(prevTaskConfig.windowConfiguration.getBounds())) {
// Reposition the UI surfaces.
- updateButtonSurfacePosition();
- updateHintSurfacePosition();
+ updateAllSurfacePositions();
}
if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
@@ -190,19 +193,14 @@
/** Called when display layout changed. */
void updateDisplayLayout(DisplayLayout displayLayout) {
- if (displayLayout == mDisplayLayout) {
- return;
- }
-
- final Rect prevStableBounds = new Rect();
+ final Rect prevStableBounds = mStableBounds;
final Rect curStableBounds = new Rect();
- mDisplayLayout.getStableBounds(prevStableBounds);
displayLayout.getStableBounds(curStableBounds);
mDisplayLayout = displayLayout;
if (!prevStableBounds.equals(curStableBounds)) {
// Stable bounds changed, update UI surface positions.
- updateButtonSurfacePosition();
- updateHintSurfacePosition();
+ updateAllSurfacePositions();
+ mStableBounds.set(curStableBounds);
}
}
@@ -268,6 +266,11 @@
createSizeCompatHint();
}
+ private void updateAllSurfacePositions() {
+ updateButtonSurfacePosition();
+ updateHintSurfacePosition();
+ }
+
@VisibleForTesting
void updateButtonSurfacePosition() {
if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) {
@@ -290,6 +293,7 @@
updateSurfacePosition(leash, positionX, positionY);
}
+ @VisibleForTesting
void updateHintSurfacePosition() {
if (mHint == null || mHintWindowManager == null
|| mHintWindowManager.getSurfaceControl() == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 4fa1ead..3345613 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -30,7 +30,10 @@
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ROOT_TASK_VANISHED;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__UNKNOWN_EXIT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
@@ -528,6 +531,8 @@
}
void exitSplitScreen(int toTopTaskId, int exitReason) {
+ if (!mMainStage.isActive()) return;
+
StageTaskListener childrenToTop = null;
if (mMainStage.containsTask(toTopTaskId)) {
childrenToTop = mMainStage;
@@ -543,6 +548,8 @@
}
private void exitSplitScreen(StageTaskListener childrenToTop, int exitReason) {
+ if (!mMainStage.isActive()) return;
+
final WindowContainerTransaction wct = new WindowContainerTransaction();
applyExitSplitScreen(childrenToTop, wct, exitReason);
}
@@ -559,6 +566,7 @@
setDividerVisibility(false);
mSplitLayout.resetDividerPosition();
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
+ Slog.i(TAG, "applyExitSplitScreen, reason = " + exitReasonToString(exitReason));
if (childrenToTop != null) {
logExitToStage(exitReason, childrenToTop == mMainStage);
} else {
@@ -1274,6 +1282,31 @@
mSplitLayout.isLandscape());
}
+ private String exitReasonToString(int exitReason) {
+ switch (exitReason) {
+ case SPLITSCREEN_UICHANGED__EXIT_REASON__UNKNOWN_EXIT:
+ return "UNKNOWN_EXIT";
+ case SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER:
+ return "DRAG_DIVIDER";
+ case SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME:
+ return "RETURN_HOME";
+ case SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED:
+ return "SCREEN_LOCKED";
+ case SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP:
+ return "SCREEN_LOCKED_SHOW_ON_TOP";
+ case SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED:
+ return "DEVICE_FOLDED";
+ case SPLITSCREEN_UICHANGED__EXIT_REASON__ROOT_TASK_VANISHED:
+ return "ROOT_TASK_VANISHED";
+ case SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED:
+ return "APP_FINISHED";
+ case SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW:
+ return "APP_DOES_NOT_SUPPORT_MULTIWINDOW";
+ default:
+ return "unknown reason, reason int = " + exitReason;
+ }
+ }
+
class StageListenerImpl implements StageTaskListener.StageListenerCallbacks {
boolean mHasRootTask = false;
boolean mVisible = false;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
index 8839f58..d12cf5c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
@@ -16,11 +16,14 @@
package com.android.wm.shell.sizecompatui;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -28,6 +31,8 @@
import android.content.Context;
import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
+import android.view.InsetsSource;
+import android.view.InsetsState;
import androidx.test.filters.SmallTest;
@@ -35,12 +40,16 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -58,12 +67,16 @@
private SizeCompatUIController mController;
private @Mock DisplayController mMockDisplayController;
+ private @Mock DisplayInsetsController mMockDisplayInsetsController;
private @Mock DisplayLayout mMockDisplayLayout;
private @Mock DisplayImeController mMockImeController;
private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
private @Mock SyncTransactionQueue mMockSyncQueue;
private @Mock SizeCompatUILayout mMockLayout;
+ @Captor
+ ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -72,7 +85,7 @@
doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId();
doReturn(TASK_ID).when(mMockLayout).getTaskId();
mController = new SizeCompatUIController(mContext, mMockDisplayController,
- mMockImeController, mMockSyncQueue) {
+ mMockDisplayInsetsController, mMockImeController, mMockSyncQueue) {
@Override
SizeCompatUILayout createLayout(Context context, int displayId, int taskId,
Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
@@ -114,7 +127,17 @@
}
@Test
+ public void testOnDisplayAdded() {
+ mController.onDisplayAdded(DISPLAY_ID);
+ mController.onDisplayAdded(DISPLAY_ID + 1);
+
+ verify(mMockDisplayInsetsController).addInsetsChangedListener(eq(DISPLAY_ID), any());
+ verify(mMockDisplayInsetsController).addInsetsChangedListener(eq(DISPLAY_ID + 1), any());
+ }
+
+ @Test
public void testOnDisplayRemoved() {
+ mController.onDisplayAdded(DISPLAY_ID);
final Configuration taskConfig = new Configuration();
mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
mMockTaskListener);
@@ -122,9 +145,12 @@
mController.onDisplayRemoved(DISPLAY_ID + 1);
verify(mMockLayout, never()).release();
+ verify(mMockDisplayInsetsController, never()).removeInsetsChangedListener(eq(DISPLAY_ID),
+ any());
mController.onDisplayRemoved(DISPLAY_ID);
+ verify(mMockDisplayInsetsController).removeInsetsChangedListener(eq(DISPLAY_ID), any());
verify(mMockLayout).release();
}
@@ -145,6 +171,29 @@
}
@Test
+ public void testInsetsChanged() {
+ mController.onDisplayAdded(DISPLAY_ID);
+ final Configuration taskConfig = new Configuration();
+ mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
+ mMockTaskListener);
+ InsetsState insetsState = new InsetsState();
+ InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
+ insetsSource.setFrame(0, 0, 1000, 1000);
+ insetsState.addSource(insetsSource);
+
+ verify(mMockDisplayInsetsController).addInsetsChangedListener(eq(DISPLAY_ID),
+ mOnInsetsChangedListenerCaptor.capture());
+ mOnInsetsChangedListenerCaptor.getValue().insetsChanged(insetsState);
+
+ verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);
+
+ // No update if the insets state is the same.
+ clearInvocations(mMockLayout);
+ mOnInsetsChangedListenerCaptor.getValue().insetsChanged(new InsetsState(insetsState));
+ verify(mMockLayout, never()).updateDisplayLayout(mMockDisplayLayout);
+ }
+
+ @Test
public void testChangeButtonVisibilityOnImeShowHide() {
final Configuration taskConfig = new Configuration();
mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
index ee4c815..2ba603c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
@@ -16,6 +16,8 @@
package com.android.wm.shell.sizecompatui;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertFalse;
@@ -33,6 +35,8 @@
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.view.DisplayInfo;
+import android.view.InsetsSource;
+import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.View;
@@ -77,7 +81,7 @@
mTaskConfig = new Configuration();
mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
- new Configuration(), TASK_ID, mTaskListener, mDisplayLayout,
+ new Configuration(), TASK_ID, mTaskListener, new DisplayLayout(),
false /* hasShownHint */);
spyOn(mLayout);
@@ -176,7 +180,7 @@
displayInfo.logicalWidth = 1000;
displayInfo.logicalHeight = 2000;
final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo,
- mContext.getResources(), false, false);
+ mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
mLayout.updateDisplayLayout(displayLayout1);
verify(mLayout).updateButtonSurfacePosition();
@@ -185,13 +189,37 @@
// No update if the display bounds is the same.
clearInvocations(mLayout);
final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo,
- mContext.getResources(), false, false);
+ mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
mLayout.updateDisplayLayout(displayLayout2);
verify(mLayout, never()).updateButtonSurfacePosition();
verify(mLayout, never()).updateHintSurfacePosition();
}
@Test
+ public void testUpdateDisplayLayoutInsets() {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = 1000;
+ displayInfo.logicalHeight = 2000;
+ final DisplayLayout displayLayout = new DisplayLayout(displayInfo,
+ mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false);
+
+ mLayout.updateDisplayLayout(displayLayout);
+ verify(mLayout).updateButtonSurfacePosition();
+ verify(mLayout).updateHintSurfacePosition();
+
+ // Update if the insets change on the existing display layout
+ clearInvocations(mLayout);
+ InsetsState insetsState = new InsetsState();
+ InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
+ insetsSource.setFrame(0, 0, 1000, 1000);
+ insetsState.addSource(insetsSource);
+ displayLayout.setInsets(mContext.getResources(), insetsState);
+ mLayout.updateDisplayLayout(displayLayout);
+ verify(mLayout).updateButtonSurfacePosition();
+ verify(mLayout).updateHintSurfacePosition();
+ }
+
+ @Test
public void testUpdateImeVisibility() {
// Create button if it is not created.
mLayout.mButton = null;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 617e94a..e71fa94 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -161,6 +161,7 @@
@Test
public void testExitSplitScreen() {
+ when(mMainStage.isActive()).thenReturn(true);
mStageCoordinator.exitSplitScreen(INVALID_TASK_ID,
SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME);
verify(mSideStage).removeAllTasks(any(WindowContainerTransaction.class), eq(false));
@@ -169,6 +170,7 @@
@Test
public void testExitSplitScreenToMainStage() {
+ when(mMainStage.isActive()).thenReturn(true);
final int testTaskId = 12345;
when(mMainStage.containsTask(eq(testTaskId))).thenReturn(true);
when(mSideStage.containsTask(eq(testTaskId))).thenReturn(false);
@@ -182,6 +184,7 @@
@Test
public void testExitSplitScreenToSideStage() {
+ when(mMainStage.isActive()).thenReturn(true);
final int testTaskId = 12345;
when(mMainStage.containsTask(eq(testTaskId))).thenReturn(false);
when(mSideStage.containsTask(eq(testTaskId))).thenReturn(true);
diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
index 4197934..0dd64b8 100644
--- a/media/java/android/media/tv/interactive/ITvIAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
@@ -24,4 +24,5 @@
oneway interface ITvIAppClient {
void onSessionCreated(in String iAppServiceId, IBinder token, int seq);
void onSessionReleased(int seq);
+ void onLayoutSurface(int left, int top, int right, int bottom, int seq);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
index a435e20..c7b08d5 100644
--- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
@@ -17,6 +17,7 @@
package android.media.tv.interactive;
import android.media.tv.interactive.ITvIAppClient;
+import android.view.Surface;
/**
* Interface to the TV interactive app service.
@@ -27,4 +28,7 @@
void createSession(
in ITvIAppClient client, in String iAppServiceId, int type, int seq, int userId);
void releaseSession(in IBinder sessionToken, int userId);
+ void setSurface(in IBinder sessionToken, in Surface surface, int userId);
+ void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height,
+ int userId);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppSession.aidl b/media/java/android/media/tv/interactive/ITvIAppSession.aidl
index 0cbdc8e..0afa971 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSession.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppSession.aidl
@@ -16,6 +16,8 @@
package android.media.tv.interactive;
+import android.view.Surface;
+
/**
* Sub-interface of ITvIAppService.aidl which is created per session and has its own context.
* @hide
@@ -23,4 +25,6 @@
oneway interface ITvIAppSession {
void startIApp();
void release();
+ void setSurface(in Surface surface);
+ void dispatchSurfaceChanged(int format, int width, int height);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
index b3b317e..0873aad 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
@@ -25,4 +25,5 @@
*/
oneway interface ITvIAppSessionCallback {
void onSessionCreated(in ITvIAppSession session);
+ void onLayoutSurface(int left, int top, int right, int bottom);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java
index f656534..16e19e7 100644
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvIAppManager.java
@@ -25,6 +25,7 @@
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
+import android.view.Surface;
import com.android.internal.util.Preconditions;
@@ -88,6 +89,18 @@
record.postSessionReleased();
}
}
+
+ @Override
+ public void onLayoutSurface(int left, int top, int right, int bottom, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postLayoutSurface(left, top, right, bottom);
+ }
+ }
};
}
@@ -159,6 +172,44 @@
}
/**
+ * Sets the {@link android.view.Surface} for this session.
+ *
+ * @param surface A {@link android.view.Surface} used to render video.
+ */
+ public void setSurface(Surface surface) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ // surface can be null.
+ try {
+ mService.setSurface(mToken, surface, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Notifies of any structural changes (format or size) of the surface passed in
+ * {@link #setSurface}.
+ *
+ * @param format The new PixelFormat of the surface.
+ * @param width The new width of the surface.
+ * @param height The new height of the surface.
+ */
+ public void dispatchSurfaceChanged(int format, int width, int height) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.dispatchSurfaceChanged(mToken, format, width, height, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Releases this session.
*/
public void release() {
@@ -211,6 +262,16 @@
}
});
}
+
+ void postLayoutSurface(final int left, final int top, final int right,
+ final int bottom) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onLayoutSurface(mSession, left, top, right, bottom);
+ }
+ });
+ }
}
/**
@@ -235,5 +296,18 @@
*/
public void onSessionReleased(@NonNull Session session) {
}
+
+ /**
+ * This is called when {@link TvIAppService.Session#layoutSurface} is called to change the
+ * layout of surface.
+ *
+ * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @param left Left position.
+ * @param top Top position.
+ * @param right Right position.
+ * @param bottom Bottom position.
+ */
+ public void onLayoutSurface(Session session, int left, int top, int right, int bottom) {
+ }
}
}
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
index f363728..b385d9c 100644
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -16,10 +16,12 @@
package android.media.tv.interactive;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.Service;
+import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
@@ -27,6 +29,7 @@
import android.os.RemoteException;
import android.util.Log;
import android.view.KeyEvent;
+import android.view.Surface;
import com.android.internal.os.SomeArgs;
@@ -94,6 +97,20 @@
// @GuardedBy("mLock")
private final List<Runnable> mPendingActions = new ArrayList<>();
+ private final Context mContext;
+ private final Handler mHandler;
+ private Surface mSurface;
+
+ /**
+ * Creates a new Session.
+ *
+ * @param context The context of the application
+ */
+ public Session(Context context) {
+ mContext = context;
+ mHandler = new Handler(context.getMainLooper());
+ }
+
/**
* Starts TvIAppService session.
*/
@@ -101,16 +118,79 @@
}
/**
+ * Called when the application sets the surface.
+ *
+ * <p>The TV IApp service should render interactive app UI onto the given surface. When
+ * called with {@code null}, the input service should immediately free any references to the
+ * currently set surface and stop using it.
+ *
+ * @param surface The surface to be used for interactive app UI rendering. Can be
+ * {@code null}.
+ * @return {@code true} if the surface was set successfully, {@code false} otherwise.
+ */
+ public abstract boolean onSetSurface(@Nullable Surface surface);
+
+ /**
+ * Called after any structural changes (format or size) have been made to the surface passed
+ * in {@link #onSetSurface}. This method is always called at least once, after
+ * {@link #onSetSurface} is called with non-null surface.
+ *
+ * @param format The new PixelFormat of the surface.
+ * @param width The new width of the surface.
+ * @param height The new height of the surface.
+ */
+ public void onSurfaceChanged(int format, int width, int height) {
+ }
+
+ /**
* Releases TvIAppService session.
*/
public void onRelease() {
}
+ /**
+ * Assigns a size and position to the surface passed in {@link #onSetSurface}. The position
+ * is relative to the overlay view that sits on top of this surface.
+ *
+ * @param left Left position in pixels, relative to the overlay view.
+ * @param top Top position in pixels, relative to the overlay view.
+ * @param right Right position in pixels, relative to the overlay view.
+ * @param bottom Bottom position in pixels, relative to the overlay view.
+ */
+ public void layoutSurface(final int left, final int top, final int right,
+ final int bottom) {
+ if (left > right || top > bottom) {
+ throw new IllegalArgumentException("Invalid parameter");
+ }
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "layoutSurface (l=" + left + ", t=" + top
+ + ", r=" + right + ", b=" + bottom + ",)");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onLayoutSurface(left, top, right, bottom);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in layoutSurface", e);
+ }
+ }
+ });
+ }
+
void startIApp() {
onStartIApp();
}
+
void release() {
onRelease();
+ if (mSurface != null) {
+ mSurface.release();
+ mSurface = null;
+ }
}
private void initialize(ITvIAppSessionCallback callback) {
@@ -122,6 +202,45 @@
mPendingActions.clear();
}
}
+
+ /**
+ * Calls {@link #onSetSurface}.
+ */
+ void setSurface(Surface surface) {
+ onSetSurface(surface);
+ if (mSurface != null) {
+ mSurface.release();
+ }
+ mSurface = surface;
+ // TODO: Handle failure.
+ }
+
+ /**
+ * Calls {@link #onSurfaceChanged}.
+ */
+ void dispatchSurfaceChanged(int format, int width, int height) {
+ if (DEBUG) {
+ Log.d(TAG, "dispatchSurfaceChanged(format=" + format + ", width=" + width
+ + ", height=" + height + ")");
+ }
+ onSurfaceChanged(format, width, height);
+ }
+
+ private void executeOrPostRunnableOnMainThread(Runnable action) {
+ synchronized (mLock) {
+ if (mSessionCallback == null) {
+ // The session is not initialized yet.
+ mPendingActions.add(action);
+ } else {
+ if (mHandler.getLooper().isCurrentThread()) {
+ action.run();
+ } else {
+ // Posts the runnable if this is not called from the main thread
+ mHandler.post(action);
+ }
+ }
+ }
+ }
}
/**
@@ -143,6 +262,16 @@
public void release() {
mSessionImpl.release();
}
+
+ @Override
+ public void setSurface(Surface surface) {
+ mSessionImpl.setSurface(surface);
+ }
+
+ @Override
+ public void dispatchSurfaceChanged(int format, int width, int height) {
+ mSessionImpl.dispatchSurfaceChanged(format, width, height);
+ }
}
@SuppressLint("HandlerLeak")
diff --git a/media/java/android/media/tv/interactive/TvIAppView.java b/media/java/android/media/tv/interactive/TvIAppView.java
index f56ea0a..adaaab0 100644
--- a/media/java/android/media/tv/interactive/TvIAppView.java
+++ b/media/java/android/media/tv/interactive/TvIAppView.java
@@ -17,10 +17,18 @@
package android.media.tv.interactive;
import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
import android.media.tv.interactive.TvIAppManager.Session;
import android.media.tv.interactive.TvIAppManager.SessionCallback;
import android.os.Handler;
+import android.util.AttributeSet;
import android.util.Log;
+import android.util.Xml;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
import android.view.ViewGroup;
/**
@@ -35,18 +43,137 @@
private final Handler mHandler = new Handler();
private Session mSession;
private MySessionCallback mSessionCallback;
+ private SurfaceView mSurfaceView;
+ private Surface mSurface;
+
+ private boolean mSurfaceChanged;
+ private int mSurfaceFormat;
+ private int mSurfaceWidth;
+ private int mSurfaceHeight;
+
+ private boolean mUseRequestedSurfaceLayout;
+ private int mSurfaceViewLeft;
+ private int mSurfaceViewRight;
+ private int mSurfaceViewTop;
+ private int mSurfaceViewBottom;
+
+ private final AttributeSet mAttrs;
+ private final int mDefStyleAttr;
+ private final XmlResourceParser mParser;
+
+ private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ if (DEBUG) {
+ Log.d(TAG, "surfaceChanged(holder=" + holder + ", format=" + format
+ + ", width=" + width + ", height=" + height + ")");
+ }
+ mSurfaceFormat = format;
+ mSurfaceWidth = width;
+ mSurfaceHeight = height;
+ mSurfaceChanged = true;
+ dispatchSurfaceChanged(mSurfaceFormat, mSurfaceWidth, mSurfaceHeight);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mSurface = holder.getSurface();
+ setSessionSurface(mSurface);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mSurface = null;
+ mSurfaceChanged = false;
+ setSessionSurface(null);
+ }
+ };
public TvIAppView(Context context) {
+ this(context, null, 0);
+ }
+
+ public TvIAppView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, /* attrs = */null, /* defStyleAttr = */0);
+ int sourceResId = Resources.getAttributeSetSourceResId(attrs);
+ if (sourceResId != Resources.ID_NULL) {
+ Log.d(TAG, "Build local AttributeSet");
+ mParser = context.getResources().getXml(sourceResId);
+ mAttrs = Xml.asAttributeSet(mParser);
+ } else {
+ Log.d(TAG, "Use passed in AttributeSet");
+ mParser = null;
+ mAttrs = attrs;
+ }
+ mDefStyleAttr = defStyleAttr;
+ resetSurfaceView();
mTvIAppManager = (TvIAppManager) getContext().getSystemService("tv_interactive_app");
}
@Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (DEBUG) {
- Log.d(TAG,
- "onLayout (left=" + l + ", top=" + t + ", right=" + r + ", bottom=" + b + ",)");
+ Log.d(TAG, "onLayout (left=" + left + ", top=" + top + ", right=" + right
+ + ", bottom=" + bottom + ",)");
}
+ if (mUseRequestedSurfaceLayout) {
+ mSurfaceView.layout(mSurfaceViewLeft, mSurfaceViewTop, mSurfaceViewRight,
+ mSurfaceViewBottom);
+ } else {
+ mSurfaceView.layout(0, 0, right - left, bottom - top);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mSurfaceView.measure(widthMeasureSpec, heightMeasureSpec);
+ int width = mSurfaceView.getMeasuredWidth();
+ int height = mSurfaceView.getMeasuredHeight();
+ int childState = mSurfaceView.getMeasuredState();
+ setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, childState),
+ resolveSizeAndState(height, heightMeasureSpec,
+ childState << MEASURED_HEIGHT_STATE_SHIFT));
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ mSurfaceView.setVisibility(visibility);
+ }
+
+ private void resetSurfaceView() {
+ if (mSurfaceView != null) {
+ mSurfaceView.getHolder().removeCallback(mSurfaceHolderCallback);
+ removeView(mSurfaceView);
+ }
+ mSurface = null;
+ mSurfaceView = new SurfaceView(getContext(), mAttrs, mDefStyleAttr);
+ // The surface view's content should be treated as secure all the time.
+ mSurfaceView.setSecure(true);
+ mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback);
+ addView(mSurfaceView);
+ }
+
+ /**
+ * Resets this TvIAppView.
+ */
+ public void reset() {
+ if (DEBUG) Log.d(TAG, "reset()");
+ resetInternal();
+ }
+
+ private void setSessionSurface(Surface surface) {
+ if (mSession == null) {
+ return;
+ }
+ mSession.setSurface(surface);
+ }
+
+ private void dispatchSurfaceChanged(int format, int width, int height) {
+ if (mSession == null) {
+ return;
+ }
+ mSession.dispatchSurfaceChanged(format, width, height);
}
/**
@@ -75,6 +202,17 @@
}
}
+ private void resetInternal() {
+ mSessionCallback = null;
+ if (mSession != null) {
+ setSessionSurface(null);
+ mUseRequestedSurfaceLayout = false;
+ mSession.release();
+ mSession = null;
+ resetSurfaceView();
+ }
+ }
+
private class MySessionCallback extends SessionCallback {
final String mIAppServiceId;
int mType;
@@ -99,7 +237,15 @@
}
mSession = session;
if (session != null) {
- // TODO: handle SurfaceView and InputChannel.
+ // mSurface may not be ready yet as soon as starting an application.
+ // In the case, we don't send Session.setSurface(null) unnecessarily.
+ // setSessionSurface will be called in surfaceCreated.
+ if (mSurface != null) {
+ setSessionSurface(mSurface);
+ if (mSurfaceChanged) {
+ dispatchSurfaceChanged(mSurfaceFormat, mSurfaceWidth, mSurfaceHeight);
+ }
+ }
} else {
// Failed to create
// Todo: forward error to Tv App
@@ -119,5 +265,23 @@
mSessionCallback = null;
mSession = null;
}
+
+ @Override
+ public void onLayoutSurface(Session session, int left, int top, int right, int bottom) {
+ if (DEBUG) {
+ Log.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top + ", right="
+ + right + ", bottom=" + bottom + ",)");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onLayoutSurface - session not created");
+ return;
+ }
+ mSurfaceViewLeft = left;
+ mSurfaceViewTop = top;
+ mSurfaceViewRight = right;
+ mSurfaceViewBottom = bottom;
+ mUseRequestedSurfaceLayout = true;
+ requestLayout();
+ }
}
}
diff --git a/packages/SettingsLib/ActivityEmbedding/Android.bp b/packages/SettingsLib/ActivityEmbedding/Android.bp
new file mode 100644
index 0000000..fc82b79
--- /dev/null
+++ b/packages/SettingsLib/ActivityEmbedding/Android.bp
@@ -0,0 +1,21 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+ name: "SettingsLibActivityEmbedding",
+
+ srcs: ["src/**/*.java"],
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ "SettingsLibUtils",
+ ],
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/ActivityEmbedding/AndroidManifest.xml b/packages/SettingsLib/ActivityEmbedding/AndroidManifest.xml
new file mode 100644
index 0000000..2e6c405
--- /dev/null
+++ b/packages/SettingsLib/ActivityEmbedding/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.activityembedding">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
new file mode 100644
index 0000000..36c2bda
--- /dev/null
+++ b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.activityembedding;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+
+import com.android.settingslib.utils.BuildCompatUtils;
+
+/**
+ * An util class collecting all common methods for the embedding activity features.
+ */
+public class ActivityEmbeddingUtils {
+ private static final String ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY =
+ "android.settings.SETTINGS_EMBED_DEEP_LINK_ACTIVITY";
+ private static final String PACKAGE_NAME_SETTINGS = "com.android.settings";
+
+ /**
+ * Whether to support embedding activity feature.
+ */
+ public static boolean isEmbeddingActivityEnabled(Context context) {
+ if (BuildCompatUtils.isAtLeastS()) {
+ final Intent intent = new Intent(ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY);
+ intent.setPackage(PACKAGE_NAME_SETTINGS);
+ final ResolveInfo resolveInfo =
+ context.getPackageManager().resolveActivity(intent, 0 /* flags */);
+ return resolveInfo != null
+ && resolveInfo.activityInfo != null
+ && resolveInfo.activityInfo.enabled;
+ }
+ return false;
+ }
+
+ private ActivityEmbeddingUtils() {
+ }
+}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index e8ed88f..b266df5 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -48,6 +48,7 @@
"SettingsLibCollapsingToolbarBaseActivity",
"SettingsLibTwoTargetPreference",
"SettingsLibSettingsTransition",
+ "SettingsLibActivityEmbedding",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
index 6da249c..c3f845c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
@@ -59,7 +59,7 @@
// These callbacks run on the main thread.
private final class CoordinatedSetServiceListener implements BluetoothProfile.ServiceListener {
- @RequiresApi(32)
+ @RequiresApi(33)
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (VDBG) {
Log.d(TAG, "Bluetooth service connected");
@@ -233,7 +233,7 @@
return NAME;
}
- @RequiresApi(32)
+ @RequiresApi(33)
protected void finalize() {
if (VDBG) {
Log.d(TAG, "finalize()");
diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml
index e2d5229..f6e3f1a 100644
--- a/packages/SystemUI/res/layout/screenshot_static.xml
+++ b/packages/SystemUI/res/layout/screenshot_static.xml
@@ -94,7 +94,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:elevation="@dimen/screenshot_preview_elevation"
- android:contentDescription="@string/screenshot_edit_label"
+ android:contentDescription="@string/screenshot_edit_description"
android:scaleType="fitEnd"
android:background="@drawable/screenshot_preview_background"
android:adjustViewBounds="true"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index dee886a..6348f3a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -183,6 +183,9 @@
<!-- Label for UI element which allows editing the screenshot [CHAR LIMIT=30] -->
<string name="screenshot_edit_label">Edit</string>
<!-- Content description indicating that tapping the element will allow editing the screenshot [CHAR LIMIT=NONE] -->
+ <string name="screenshot_edit_description">Edit screenshot</string>
+ <!-- Content description indicating that tapping the element will allow sharing the screenshot [CHAR LIMIT=NONE] -->
+ <string name="screenshot_share_description">Share screenshot</string>
<!-- Label for UI element which allows the user to capture additional off-screen content in a screenshot. [CHAR LIMIT=30] -->
<string name="screenshot_scroll_label">Capture more</string>
<!-- Content description indicating that tapping a button will dismiss the screenshots UI [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index a0118ab..6e3d6a8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -776,6 +776,10 @@
}
mWindowManager.removeViewImmediate(decorView);
}
+ // Ensure that we remove the input monitor
+ if (mScreenshotView != null) {
+ mScreenshotView.stopInputListening();
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 3314c75..fbc6790 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -314,7 +314,7 @@
});
}
- private void stopInputListening() {
+ void stopInputListening() {
if (mInputMonitor != null) {
mInputMonitor.dispose();
mInputMonitor = null;
@@ -622,7 +622,7 @@
ArrayList<ScreenshotActionChip> chips = new ArrayList<>();
- mShareChip.setContentDescription(mContext.getString(com.android.internal.R.string.share));
+ mShareChip.setContentDescription(mContext.getString(R.string.screenshot_share_description));
mShareChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_share), true);
mShareChip.setOnClickListener(v -> {
mShareChip.setIsPending(true);
@@ -634,7 +634,7 @@
});
chips.add(mShareChip);
- mEditChip.setContentDescription(mContext.getString(R.string.screenshot_edit_label));
+ mEditChip.setContentDescription(mContext.getString(R.string.screenshot_edit_description));
mEditChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_edit), true);
mEditChip.setOnClickListener(v -> {
mEditChip.setIsPending(true);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index bdc62c9..b85ceefb 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -156,9 +156,10 @@
@WMSingleton
@Provides
static SizeCompatUIController provideSizeCompatUIController(Context context,
- DisplayController displayController, DisplayImeController imeController,
- SyncTransactionQueue syncQueue) {
- return new SizeCompatUIController(context, displayController, imeController, syncQueue);
+ DisplayController displayController, DisplayInsetsController displayInsetsController,
+ DisplayImeController imeController, SyncTransactionQueue syncQueue) {
+ return new SizeCompatUIController(context, displayController, displayInsetsController,
+ imeController, syncQueue);
}
@WMSingleton
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index bc51774..cebabcb 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -775,12 +775,14 @@
exemptFromAutoRevoke(packageInfo.packageName, packageInfo.applicationInfo.uid);
- if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddress())) {
- grantDeviceProfile(association);
- }
+ if (!association.isManagedByCompanionApp()) {
+ if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddress())) {
+ grantDeviceProfile(association);
+ }
- if (association.isNotifyOnDeviceNearby()) {
- restartBleScan();
+ if (association.isNotifyOnDeviceNearby()) {
+ restartBleScan();
+ }
}
}
@@ -1217,6 +1219,9 @@
ArrayList<ScanFilter> result = new ArrayList<>();
ArraySet<String> addressesSeen = new ArraySet<>();
for (AssociationInfo association : getAllAssociations()) {
+ if (association.isManagedByCompanionApp()) {
+ continue;
+ }
String address = association.getDeviceMacAddress();
if (addressesSeen.contains(address)) {
continue;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 53f7575..cf4d048 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13302,7 +13302,7 @@
mAtmInternal.removeRecentTasksByPackageName(ssp, userId);
mServices.forceStopPackageLocked(ssp, userId);
- mAtmInternal.onPackageUninstalled(ssp);
+ mAtmInternal.onPackageUninstalled(ssp, userId);
mBatteryStatsService.notePackageUninstalled(ssp);
}
} else {
@@ -13387,7 +13387,7 @@
Uri data = intent.getData();
String ssp;
if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
- mAtmInternal.onPackageDataCleared(ssp);
+ mAtmInternal.onPackageDataCleared(ssp, userId);
}
break;
}
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 4ef36d6..b5a9ee9 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -96,6 +96,7 @@
DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
DeviceConfig.NAMESPACE_SURFACE_FLINGER_NATIVE_BOOT,
DeviceConfig.NAMESPACE_SWCODEC_NATIVE,
+ DeviceConfig.NAMESPACE_TETHERING,
DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
};
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 5ce72c2..9d2cff9 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -108,9 +108,8 @@
/**
* When enabled this change id forces the packages it is applied to override the default
- * camera rotate & crop behavior and always return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE .
- * The default behavior along with all possible override combinations is discussed in the table
- * below.
+ * camera rotate & crop behavior. The default behavior along with all possible override
+ * combinations is discussed in the table below.
*/
@ChangeId
@Overridable
@@ -122,7 +121,9 @@
* When enabled this change id forces the packages it is applied to ignore the current value of
* 'android:resizeableActivity' as well as target SDK equal to or below M and consider the
* activity as non-resizeable. In this case, the value of camera rotate & crop will only depend
- * on the needed compensation considering the current display rotation.
+ * on potential mismatches between the orientation of the camera and the fixed orientation of
+ * the activity. You can check the table below for further details on the possible override
+ * combinations.
*/
@ChangeId
@Overridable
@@ -131,30 +132,67 @@
public static final long OVERRIDE_CAMERA_RESIZABLE_AND_SDK_CHECK = 191513214L; // buganizer id
/**
+ * This change id forces the packages it is applied to override the default camera rotate & crop
+ * behavior. Enabling it will set the crop & rotate parameter to
+ * {@link android.hardware.camera2.CaptureRequest#SCALER_ROTATE_AND_CROP_90} and disabling it
+ * will reset the parameter to
+ * {@link android.hardware.camera2.CaptureRequest#SCALER_ROTATE_AND_CROP_NONE} as long as camera
+ * clients include {@link android.hardware.camera2.CaptureRequest#SCALER_ROTATE_AND_CROP_AUTO}
+ * in their capture requests.
+ *
+ * This treatment only takes effect if OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS is also enabled.
+ * The table below includes further information about the possible override combinations.
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ @TestApi
+ public static final long OVERRIDE_CAMERA_ROTATE_AND_CROP = 190069291L; //buganizer id
+
+ /**
* Possible override combinations
*
- * |OVERRIDE |OVERRIDE_
- * |CAMERA_ |CAMERA_
- * |ROTATE_ |RESIZEABLE_
- * |AND_CROP_ |AND_SDK_
- * |DEFAULTS |CHECK
- * _________________________________________________
- * Default Behavior | D |D
- * _________________________________________________
- * Ignore SDK&Resize | D |E
- * _________________________________________________
- * SCALER_ROTATE_AND_CROP_NONE | E |D, E
- * _________________________________________________
+ * |OVERRIDE | |OVERRIDE_
+ * |CAMERA_ |OVERRIDE |CAMERA_
+ * |ROTATE_ |CAMERA_ |RESIZEABLE_
+ * |AND_CROP_ |ROTATE_ |AND_SDK_
+ * |DEFAULTS |AND_CROP |CHECK
+ * ______________________________________________
+ * Default | | |
+ * Behavior | D |D |D
+ * ______________________________________________
+ * Ignore | | |
+ * SDK&Resize | D |D |E
+ * ______________________________________________
+ * Default | | |
+ * Behavior | D |E |D
+ * ______________________________________________
+ * Ignore | | |
+ * SDK&Resize | D |E |E
+ * ______________________________________________
+ * Rotate&Crop| | |
+ * disabled | E |D |D
+ * ______________________________________________
+ * Rotate&Crop| | |
+ * disabled | E |D |E
+ * ______________________________________________
+ * Rotate&Crop| | |
+ * enabled | E |E |D
+ * ______________________________________________
+ * Rotate&Crop| | |
+ * enabled | E |E |E
+ * ______________________________________________
* Where:
- * E -> Override enabled
- * D -> Override disabled
- * Default behavior -> Rotate&crop will be calculated depending on the required
- * compensation necessary for the current display rotation.
- * Additionally the app must either target M (or below)
- * or is declared as non-resizeable.
- * Ignore SDK&Resize -> The Rotate&crop value will depend on the required
- * compensation for the current display rotation.
- * SCALER_ROTATE_AND_CROP_NONE -> Always return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE
+ * E -> Override enabled
+ * D -> Override disabled
+ * Default behavior -> Rotate&crop will be enabled only in cases
+ * where the fixed app orientation mismatches
+ * with the orientation of the camera.
+ * Additionally the app must either target M (or below)
+ * or is declared as non-resizeable.
+ * Ignore SDK&Resize -> Rotate&crop will be enabled only in cases
+ * where the fixed app orientation mismatches
+ * with the orientation of the camera.
*/
// Flags arguments to NFC adapter to enable/disable NFC
@@ -505,8 +543,14 @@
if ((taskInfo != null) && (CompatChanges.isChangeEnabled(
OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS, packageName,
UserHandle.getUserHandleForUid(taskInfo.userId)))) {
- Slog.v(TAG, "OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS enabled!");
+ if (CompatChanges.isChangeEnabled(OVERRIDE_CAMERA_ROTATE_AND_CROP, packageName,
+ UserHandle.getUserHandleForUid(taskInfo.userId))) {
+ Slog.v(TAG, "OVERRIDE_CAMERA_ROTATE_AND_CROP enabled!");
return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+ } else {
+ Slog.v(TAG, "OVERRIDE_CAMERA_ROTATE_AND_CROP disabled!");
+ return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+ }
}
boolean ignoreResizableAndSdkCheck = false;
if ((taskInfo != null) && (CompatChanges.isChangeEnabled(
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7e1856e..8b26419 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -811,10 +811,16 @@
@GuardedBy("mProtectedBroadcasts")
final ArraySet<String> mProtectedBroadcasts = new ArraySet<>();
- /** List of packages waiting for verification. */
+ /**
+ * List of packages waiting for verification.
+ * Handler thread only!
+ */
final SparseArray<PackageVerificationState> mPendingVerification = new SparseArray<>();
- /** List of packages waiting for rollback to be enabled. */
+ /**
+ * List of packages waiting for rollback to be enabled.
+ * Handler thread only!
+ */
final SparseArray<VerificationParams> mPendingEnableRollback = new SparseArray<>();
final PackageInstallerService mInstallerService;
@@ -835,10 +841,16 @@
// Cache of users who need badging.
private final SparseBooleanArray mUserNeedsBadging = new SparseBooleanArray();
- /** Token for keys in mPendingVerification. */
+ /**
+ * Token for keys in mPendingVerification.
+ * Handler thread only!
+ */
int mPendingVerificationToken = 0;
- /** Token for keys in mPendingEnableRollback. */
+ /**
+ * Token for keys in mPendingEnableRollback.
+ * Handler thread only!
+ */
int mPendingEnableRollbackToken = 0;
@Watched(manual = true)
@@ -6347,25 +6359,28 @@
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
"Only package verification agents can extend verification timeouts");
- final PackageVerificationState state = mPendingVerification.get(id);
- final PackageVerificationResponse response = new PackageVerificationResponse(
- verificationCodeAtTimeout, Binder.getCallingUid());
+ mHandler.post(() -> {
+ final PackageVerificationState state = mPendingVerification.get(id);
+ final PackageVerificationResponse response = new PackageVerificationResponse(
+ verificationCodeAtTimeout, Binder.getCallingUid());
- if (millisecondsToDelay > PackageManager.MAXIMUM_VERIFICATION_TIMEOUT) {
- millisecondsToDelay = PackageManager.MAXIMUM_VERIFICATION_TIMEOUT;
- }
- if (millisecondsToDelay < 0) {
- millisecondsToDelay = 0;
- }
+ long delay = millisecondsToDelay;
+ if (delay > PackageManager.MAXIMUM_VERIFICATION_TIMEOUT) {
+ delay = PackageManager.MAXIMUM_VERIFICATION_TIMEOUT;
+ }
+ if (delay < 0) {
+ delay = 0;
+ }
- if ((state != null) && !state.timeoutExtended()) {
- state.extendTimeout();
+ if ((state != null) && !state.timeoutExtended()) {
+ state.extendTimeout();
- final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
- msg.arg1 = id;
- msg.obj = response;
- mHandler.sendMessageDelayed(msg, millisecondsToDelay);
- }
+ final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
+ msg.arg1 = id;
+ msg.obj = response;
+ mHandler.sendMessageDelayed(msg, delay);
+ }
+ });
}
private void setEnableRollbackCode(int token, int enableRollbackCode) {
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 7c3b733e..9cb8863 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -527,26 +527,6 @@
}
}
- /**
- * Returns id of a committed and non-finalized stated session that contains same
- * {@code packageName}, or {@code -1} if no sessions have this {@code packageName} staged.
- */
- int getSessionIdByPackageName(@NonNull String packageName) {
- synchronized (mStagedSessions) {
- for (int i = 0; i < mStagedSessions.size(); i++) {
- StagedSession stagedSession = mStagedSessions.valueAt(i);
- if (!stagedSession.isCommitted() || stagedSession.isDestroyed()
- || stagedSession.isInTerminalState()) {
- continue;
- }
- if (stagedSession.getPackageName().equals(packageName)) {
- return stagedSession.sessionId();
- }
- }
- }
- return -1;
- }
-
@VisibleForTesting
void createSession(@NonNull StagedSession sessionInfo) {
synchronized (mStagedSessions) {
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
index d09eceb..cf212df 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -36,6 +36,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.SparseArray;
+import android.view.Surface;
import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
@@ -136,6 +137,16 @@
return sessionState;
}
+ @GuardedBy("mLock")
+ private ITvIAppSession getSessionLocked(SessionState sessionState) {
+ ITvIAppSession session = sessionState.mSession;
+ if (session == null) {
+ throw new IllegalStateException("Session not yet created for token "
+ + sessionState.mSessionToken);
+ }
+ return session;
+ }
+
private final class BinderService extends ITvIAppManager.Stub {
@Override
@@ -234,6 +245,55 @@
Slogf.e(TAG, "error in start", e);
}
}
+
+ @Override
+ public void setSurface(IBinder sessionToken, Surface surface, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "setSurface");
+ SessionState sessionState = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).setSurface(surface);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in setSurface", e);
+ }
+ }
+ } finally {
+ if (surface != null) {
+ // surface is not used in TvIAppManagerService.
+ surface.release();
+ }
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width,
+ int height, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "dispatchSurfaceChanged");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).dispatchSurfaceChanged(format, width,
+ height);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in dispatchSurfaceChanged", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
@GuardedBy("mLock")
@@ -616,6 +676,25 @@
}
}
+ @Override
+ public void onLayoutSurface(int left, int top, int right, int bottom) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
+ + ", right=" + right + ", bottom=" + bottom + ",)");
+ }
+ if (mSessionState.mSession == null || mSessionState.mClient == null) {
+ return;
+ }
+ try {
+ mSessionState.mClient.onLayoutSurface(left, top, right, bottom,
+ mSessionState.mSeq);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in onLayoutSurface", e);
+ }
+ }
+ }
+
@GuardedBy("mLock")
private boolean addSessionTokenToClientStateLocked(ITvIAppSession session) {
try {
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index d137436..15db790 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -11,7 +11,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -448,33 +447,31 @@
mLaunchObserver = new LaunchObserverRegistryImpl(looper);
}
+ private void logWindowState(String state, int durationSecs) {
+ mMetricsLogger.count(state, durationSecs);
+ }
+
void logWindowState() {
final long now = SystemClock.elapsedRealtime() / 1000;
if (mWindowState != WINDOW_STATE_INVALID) {
// We log even if the window state hasn't changed, because the user might remain in
// home/fullscreen move forever and we would like to track this kind of behavior
// too.
- mMetricsLogger.count(TRON_WINDOW_STATE_VARZ_STRINGS[mWindowState],
- (int) (now - mLastLogTimeSecs));
+ mLoggerHandler.sendMessage(PooledLambda.obtainMessage(
+ ActivityMetricsLogger::logWindowState, this,
+ TRON_WINDOW_STATE_VARZ_STRINGS[mWindowState], (int) (now - mLastLogTimeSecs)));
}
mLastLogTimeSecs = now;
mWindowState = WINDOW_STATE_INVALID;
- Task rootTask = mSupervisor.mRootWindowContainer.getTopDisplayFocusedRootTask();
- if (rootTask == null) {
- return;
- }
-
- if (rootTask.isActivityTypeAssistant()) {
+ final Task focusedTask = mSupervisor.mRootWindowContainer.getTopDisplayFocusedRootTask();
+ if (focusedTask == null) return;
+ if (focusedTask.isActivityTypeAssistant()) {
mWindowState = WINDOW_STATE_ASSISTANT;
return;
}
- @WindowingMode int windowingMode = rootTask.getWindowingMode();
- if (windowingMode == WINDOWING_MODE_PINNED) {
- rootTask = mSupervisor.mRootWindowContainer.findRootTaskBehind(rootTask);
- windowingMode = rootTask.getWindowingMode();
- }
+ @WindowingMode final int windowingMode = focusedTask.getWindowingMode();
switch (windowingMode) {
case WINDOWING_MODE_FULLSCREEN:
mWindowState = WINDOW_STATE_STANDARD;
@@ -491,7 +488,7 @@
break;
default:
if (windowingMode != WINDOWING_MODE_UNDEFINED) {
- throw new IllegalStateException("Unknown windowing mode for task=" + rootTask
+ Slog.wtf(TAG, "Unknown windowing mode for task=" + focusedTask
+ " windowingMode=" + windowingMode);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 64950c7..7fa9861 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -342,8 +342,8 @@
public abstract void onProcessMapped(int pid, WindowProcessController proc);
public abstract void onProcessUnMapped(int pid);
- public abstract void onPackageDataCleared(String name);
- public abstract void onPackageUninstalled(String name);
+ public abstract void onPackageDataCleared(String name, int userId);
+ public abstract void onPackageUninstalled(String name, int userId);
public abstract void onPackageAdded(String name, boolean replacing);
public abstract void onPackageReplaced(ApplicationInfo aInfo);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5220321..01a5636 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -956,7 +956,7 @@
mActivityClientController = new ActivityClientController(this);
mTaskChangeNotificationController =
- new TaskChangeNotificationController(mGlobalLock, mTaskSupervisor, mH);
+ new TaskChangeNotificationController(mTaskSupervisor, mH);
mLockTaskController = new LockTaskController(mContext, mTaskSupervisor, mH,
mTaskChangeNotificationController);
mActivityStartController = new ActivityStartController(this);
@@ -5656,19 +5656,20 @@
}
@Override
- public void onPackageDataCleared(String name) {
+ public void onPackageDataCleared(String name, int userId) {
synchronized (mGlobalLock) {
mCompatModePackages.handlePackageDataClearedLocked(name);
mAppWarnings.onPackageDataCleared(name);
+ mPackageConfigPersister.onPackageDataCleared(name, userId);
}
}
@Override
- public void onPackageUninstalled(String name) {
+ public void onPackageUninstalled(String name, int userId) {
synchronized (mGlobalLock) {
mAppWarnings.onPackageUninstalled(name);
mCompatModePackages.handlePackageUninstalledLocked(name);
- mPackageConfigPersister.onPackageUninstall(name);
+ mPackageConfigPersister.onPackageUninstall(name, userId);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 94cd77a..b35d84b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2049,6 +2049,16 @@
// Update the current top activity.
mTopResumedActivity = topRootTask.getTopResumedActivity();
+ // Update process state if there is no activity state change (e.g. focus change between
+ // multi-window mode activities) to make sure that the current top has top oom-adj.
+ // If the previous top is null, there should be activity state change from it, Then the
+ // process state should also have been updated so no need to update again.
+ if (mTopResumedActivity != null && prevTopActivity != null) {
+ if (mTopResumedActivity.app != null) {
+ mTopResumedActivity.app.addToPendingTop();
+ }
+ mService.updateOomAdj();
+ }
scheduleTopResumedActivityStateIfNeeded();
mService.updateTopApp(mTopResumedActivity);
@@ -2206,10 +2216,6 @@
task.mTaskId, reason, topActivity.info.applicationInfo.packageName);
}
- void logRootTaskState() {
- mActivityMetricsLogger.logWindowState();
- }
-
void scheduleUpdateMultiWindowMode(Task task) {
final PooledConsumer c = PooledLambda.obtainConsumer(
ActivityTaskSupervisor::addToMultiWindowModeChangedList, this,
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7d9fe21..241e7e2 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4930,6 +4930,7 @@
if (imeTarget != null && !(imeTarget.mActivityRecord != null
&& imeTarget.mActivityRecord.hasStartingWindow())) {
final boolean canImeTargetSetRelativeLayer = imeTarget.getSurfaceControl() != null
+ && imeTarget == mImeControlTarget
&& !imeTarget.inMultiWindowMode()
&& imeTarget.mToken.getActivity(app -> app.isAnimating(TRANSITION | PARENTS,
ANIMATION_TYPE_ALL & ~ANIMATION_TYPE_RECENTS)) == null;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 7307d64..5719f46 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -93,7 +93,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import android.Manifest.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Px;
@@ -103,7 +102,6 @@
import android.app.ResourcesManager;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.PixelFormat;
@@ -843,11 +841,6 @@
return true;
}
- private boolean hasStatusBarServicePermission(int pid, int uid) {
- return mContext.checkPermission(permission.STATUS_BAR_SERVICE, pid, uid)
- == PackageManager.PERMISSION_GRANTED;
- }
-
/**
* Only trusted overlays are allowed to use FLAG_SLIPPERY.
*/
@@ -1098,9 +1091,10 @@
mNavigationBar = win;
mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
(displayFrames, windowState, inOutFrame) -> {
- inOutFrame.inset(windowState.getLayoutingAttrs(
- displayFrames.mRotation).providedInternalInsets);
-
+ if (!mNavButtonForcedVisible) {
+ inOutFrame.inset(windowState.getLayoutingAttrs(
+ displayFrames.mRotation).providedInternalInsets);
+ }
},
// For IME we use regular frame.
@@ -1799,7 +1793,6 @@
final int upsideDownRotation = displayRotation.getUpsideDownRotation();
final int landscapeRotation = displayRotation.getLandscapeRotation();
final int seascapeRotation = displayRotation.getSeascapeRotation();
- final int uiMode = mService.mPolicy.getUiMode();
if (hasStatusBar()) {
mStatusBarHeightForRotation[portraitRotation] =
@@ -2152,16 +2145,6 @@
return mNavigationBarPosition;
}
- @WindowManagerPolicy.AltBarPosition
- int getAlternateStatusBarPosition() {
- return mStatusBarAltPosition;
- }
-
- @WindowManagerPolicy.AltBarPosition
- int getAlternateNavBarPosition() {
- return mNavigationBarAltPosition;
- }
-
/**
* A new window has been focused.
*/
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 34e8149..b6552cb 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -1330,7 +1330,7 @@
case ActivityInfo.SCREEN_ORIENTATION_USER:
case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
// Works with any rotation except upside down.
- return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation);
+ return (preferredRotation >= 0) && (preferredRotation != Surface.ROTATION_180);
}
return false;
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 4595464..af91726 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -108,6 +108,16 @@
private final boolean mControllable;
+ /**
+ * Whether to forced the dimensions of the source window to the inset frame and crop out any
+ * overflow.
+ * Used to crop the taskbar inset source when a task animation is occurring to hide the taskbar
+ * rounded corners overlays.
+ *
+ * TODO: Remove when we enable shell transitions (b/202383002)
+ */
+ private boolean mCropToProvidingInsets = false;
+
InsetsSourceProvider(InsetsSource source, InsetsStateController stateController,
DisplayContent displayContent) {
mClientVisible = InsetsState.getDefaultVisibility(source.getType());
@@ -303,6 +313,62 @@
mFakeControlTarget = fakeTarget;
}
+ /**
+ * Ensures that the inset source window is cropped so that anything that doesn't fit within the
+ * inset frame is cropped out until removeCropToProvidingInsetsBounds is called.
+ *
+ * The inset source surface will get cropped to the be of the size of the insets it's providing.
+ *
+ * For example, for the taskbar window which serves as the ITYPE_EXTRA_NAVIGATION_BAR inset
+ * source, the window is larger than the insets because of the rounded corners overlay, but
+ * during task animations we want to make sure that the overlay is cropped out of the window so
+ * that they don't hide the window animations.
+ *
+ * @param t The transaction to use to apply immediate overflow cropping operations.
+ *
+ * NOTE: The relies on the inset source window to have a leash (usually this would be a leash
+ * for the ANIMATION_TYPE_INSETS_CONTROL animation if the inset is controlled by the client)
+ *
+ * TODO: Remove when we migrate over to shell transitions (b/202383002)
+ */
+ void setCropToProvidingInsetsBounds(Transaction t) {
+ mCropToProvidingInsets = true;
+
+ if (mWin != null && mWin.mSurfaceAnimator.hasLeash()) {
+ // apply to existing leash
+ t.setWindowCrop(mWin.mSurfaceAnimator.mLeash, getProvidingInsetsBoundsCropRect());
+ }
+ }
+
+ /**
+ * Removes any overflow cropping and future cropping to the inset source window's leash that may
+ * have been set with a call to setCropToProvidingInsetsBounds().
+ * @param t The transaction to use to apply immediate removal of overflow cropping.
+ *
+ * TODO: Remove when we migrate over to shell transitions (b/202383002)
+ */
+ void removeCropToProvidingInsetsBounds(Transaction t) {
+ mCropToProvidingInsets = false;
+
+ // apply to existing leash
+ if (mWin != null && mWin.mSurfaceAnimator.hasLeash()) {
+ t.setWindowCrop(mWin.mSurfaceAnimator.mLeash, null);
+ }
+ }
+
+ private Rect getProvidingInsetsBoundsCropRect() {
+ Rect sourceWindowFrame = mWin.getFrame();
+ Rect insetFrame = getSource().getFrame();
+
+ // The rectangle in buffer space we want to crop to
+ return new Rect(
+ insetFrame.left - sourceWindowFrame.left,
+ insetFrame.top - sourceWindowFrame.top,
+ insetFrame.right - sourceWindowFrame.left,
+ insetFrame.bottom - sourceWindowFrame.top
+ );
+ }
+
void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) {
if (mSeamlessRotating) {
// We are un-rotating the window against the display rotation. We don't want the target
@@ -548,6 +614,11 @@
mCapturedLeash = animationLeash;
t.setPosition(mCapturedLeash, mSurfacePosition.x, mSurfacePosition.y);
+
+ if (mCropToProvidingInsets) {
+ // Apply crop to hide overflow
+ t.setWindowCrop(mCapturedLeash, getProvidingInsetsBoundsCropRect());
+ }
}
@Override
diff --git a/services/core/java/com/android/server/wm/PackageConfigPersister.java b/services/core/java/com/android/server/wm/PackageConfigPersister.java
index 6de8ef7..5145e8e 100644
--- a/services/core/java/com/android/server/wm/PackageConfigPersister.java
+++ b/services/core/java/com/android/server/wm/PackageConfigPersister.java
@@ -243,12 +243,16 @@
}
@GuardedBy("mLock")
- void onPackageUninstall(String packageName) {
+ void onPackageUninstall(String packageName, int userId) {
synchronized (mLock) {
- for (int i = mModified.size() - 1; i >= 0; i--) {
- final int userId = mModified.keyAt(i);
- removePackage(packageName, userId);
- }
+ removePackage(packageName, userId);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void onPackageDataCleared(String packageName, int userId) {
+ synchronized (mLock) {
+ removePackage(packageName, userId);
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index e10ae37..bd39e00 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2583,26 +2583,6 @@
mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
}
- Task findRootTaskBehind(Task rootTask) {
- final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- if (taskDisplayArea != null) {
- final boolean[] hasFound = new boolean[1];
- // TODO(b/175136051): should this be only the direct child root task?
- final Task rootTaskBehind = taskDisplayArea.getRootTask(task -> {
- if (hasFound[0]) {
- return true;
- }
- hasFound[0] = task == rootTask;
- return false;
- });
- if (rootTaskBehind != null) {
- return rootTaskBehind;
- }
- }
- throw new IllegalStateException("Failed to find a root task behind root task =" + rootTask
- + " in=" + taskDisplayArea);
- }
-
Configuration getDisplayOverrideConfiguration(int displayId) {
final DisplayContent displayContent = getDisplayContentOrCreate(displayId);
if (displayContent == null) {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b8b9bcc..005544b 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -387,7 +387,7 @@
final ShortcutServiceInternal shortcutService =
LocalServices.getService(ShortcutServiceInternal.class);
final Intent[] shortcutIntents = shortcutService.createShortcutIntents(
- callingUid, callingPackage, packageName, shortcutId,
+ UserHandle.getUserId(callingUid), callingPackage, packageName, shortcutId,
user.getIdentifier(), callingPid, callingUid);
if (shortcutIntents == null || shortcutIntents.length == 0) {
throw new IllegalArgumentException("Invalid shortcut id");
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9767c8b..e43c9d9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -155,6 +155,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
@@ -178,9 +179,11 @@
import android.util.TypedXmlSerializer;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
+import android.view.InsetsState;
import android.view.RemoteAnimationAdapter;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.TaskTransitionSpec;
import android.view.WindowManager;
import android.view.WindowManager.TransitionOldType;
import android.window.ITaskOrganizer;
@@ -2804,6 +2807,30 @@
return;
}
+ /**
+ * Account for specified insets to crop the animation bounds by to avoid the animation
+ * occurring over "out of bounds" regions
+ *
+ * For example this is used to make sure the tasks are cropped to be fully above the
+ * taskbar when animating.
+ *
+ * @param animationBounds The animations bounds to adjust to account for the custom spec insets.
+ */
+ void adjustAnimationBoundsForTransition(Rect animationBounds) {
+ TaskTransitionSpec spec = mWmService.mTaskTransitionSpec;
+ if (spec != null) {
+ for (@InsetsState.InternalInsetsType int insetType : spec.animationBoundInsets) {
+ InsetsSourceProvider insetProvider = getDisplayContent()
+ .getInsetsStateController()
+ .getSourceProvider(insetType);
+
+ Insets insets = insetProvider.getSource().calculateVisibleInsets(
+ animationBounds);
+ animationBounds.inset(insets);
+ }
+ }
+ }
+
void setDragResizing(boolean dragResizing, int dragResizeMode) {
if (mDragResizing != dragResizing) {
// No need to check if the mode is allowed if it's leaving dragResize
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 04ec4bd..3b79274 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -29,12 +29,12 @@
import android.os.RemoteException;
import android.window.TaskSnapshot;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.SomeArgs;
import java.util.ArrayList;
class TaskChangeNotificationController {
- private static final int LOG_TASK_STATE_MSG = 1;
private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 2;
private static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 3;
private static final int NOTIFY_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4;
@@ -64,12 +64,11 @@
// Delay in notifying task stack change listeners (in millis)
private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
- // Global lock used by the service the instantiate objects of this class.
- private final Object mServiceLock;
private final ActivityTaskSupervisor mTaskSupervisor;
private final Handler mHandler;
// Task stack change listeners in a remote process.
+ @GuardedBy("mRemoteTaskStackListeners")
private final RemoteCallbackList<ITaskStackListener> mRemoteTaskStackListeners =
new RemoteCallbackList<>();
@@ -77,6 +76,7 @@
* Task stack change listeners in a local process. Tracked separately so that they can be
* called on the same thread.
*/
+ @GuardedBy("mLocalTaskStackListeners")
private final ArrayList<ITaskStackListener> mLocalTaskStackListeners = new ArrayList<>();
private final TaskStackConsumer mNotifyTaskStackChanged = (l, m) -> {
@@ -195,12 +195,6 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case LOG_TASK_STATE_MSG: {
- synchronized (mServiceLock) {
- mTaskSupervisor.logRootTaskState();
- }
- break;
- }
case NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG:
forAllRemoteListeners(mNotifyTaskStackChanged, msg);
break;
@@ -283,41 +277,39 @@
}
}
- public TaskChangeNotificationController(Object serviceLock,
- ActivityTaskSupervisor taskSupervisor, Handler handler) {
- mServiceLock = serviceLock;
+ TaskChangeNotificationController(ActivityTaskSupervisor taskSupervisor, Handler handler) {
mTaskSupervisor = taskSupervisor;
mHandler = new MainHandler(handler.getLooper());
}
public void registerTaskStackListener(ITaskStackListener listener) {
- synchronized (mServiceLock) {
- if (listener != null) {
- if (Binder.getCallingPid() == android.os.Process.myPid()) {
- if (!mLocalTaskStackListeners.contains(listener)) {
- mLocalTaskStackListeners.add(listener);
- }
- } else {
- mRemoteTaskStackListeners.register(listener);
+ if (listener instanceof Binder) {
+ synchronized (mLocalTaskStackListeners) {
+ if (!mLocalTaskStackListeners.contains(listener)) {
+ mLocalTaskStackListeners.add(listener);
}
}
+ } else if (listener != null) {
+ synchronized (mRemoteTaskStackListeners) {
+ mRemoteTaskStackListeners.register(listener);
+ }
}
}
public void unregisterTaskStackListener(ITaskStackListener listener) {
- synchronized (mServiceLock) {
- if (listener != null) {
- if (Binder.getCallingPid() == android.os.Process.myPid()) {
- mLocalTaskStackListeners.remove(listener);
- } else {
- mRemoteTaskStackListeners.unregister(listener);
- }
+ if (listener instanceof Binder) {
+ synchronized (mLocalTaskStackListeners) {
+ mLocalTaskStackListeners.remove(listener);
+ }
+ } else if (listener != null) {
+ synchronized (mRemoteTaskStackListeners) {
+ mRemoteTaskStackListeners.unregister(listener);
}
}
}
private void forAllRemoteListeners(TaskStackConsumer callback, Message message) {
- synchronized (mServiceLock) {
+ synchronized (mRemoteTaskStackListeners) {
for (int i = mRemoteTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
try {
// Make a one-way callback to the listener
@@ -331,7 +323,7 @@
}
private void forAllLocalListeners(TaskStackConsumer callback, Message message) {
- synchronized (mServiceLock) {
+ synchronized (mLocalTaskStackListeners) {
for (int i = mLocalTaskStackListeners.size() - 1; i >= 0; i--) {
try {
callback.accept(mLocalTaskStackListeners.get(i), message);
@@ -344,7 +336,7 @@
/** Notifies all listeners when the task stack has changed. */
void notifyTaskStackChanged() {
- mHandler.sendEmptyMessage(LOG_TASK_STATE_MSG);
+ mTaskSupervisor.getActivityMetricsLogger().logWindowState();
mHandler.removeMessages(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
final Message msg = mHandler.obtainMessage(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
forAllLocalListeners(mNotifyTaskStackChanged, msg);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index a1057f4..659179c 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -774,6 +774,7 @@
boolean gotOpaqueSplitScreenPrimary = false;
boolean gotOpaqueSplitScreenSecondary = false;
boolean gotTranslucentFullscreen = false;
+ boolean gotTranslucentAdjacent = false;
boolean gotTranslucentSplitScreenPrimary = false;
boolean gotTranslucentSplitScreenSecondary = false;
boolean shouldBeVisible = true;
@@ -802,6 +803,18 @@
final boolean hasRunningActivities = hasRunningActivity(other);
if (other == this) {
+ if (!adjacentTaskFragments.isEmpty() && !gotTranslucentAdjacent) {
+ // The z-order of this TaskFragment is in middle of two adjacent TaskFragments
+ // and it cannot be visible if the TaskFragment on top is not translucent and
+ // is fully occluding this one.
+ for (int j = adjacentTaskFragments.size() - 1; j >= 0; --j) {
+ final TaskFragment taskFragment = adjacentTaskFragments.get(j);
+ if (!taskFragment.isTranslucent(starting)
+ && taskFragment.getBounds().contains(this.getBounds())) {
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+ }
+ }
// Should be visible if there is no other fragment occluding it, unless it doesn't
// have any running activities, not starting one and not home stack.
shouldBeVisible = hasRunningActivities
@@ -871,6 +884,7 @@
|| otherTaskFrag.mAdjacentTaskFragment.isTranslucent(starting)) {
// Can be visible behind a translucent adjacent TaskFragments.
gotTranslucentFullscreen = true;
+ gotTranslucentAdjacent = true;
continue;
}
// Can not be visible behind adjacent TaskFragments.
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index b4e07d3..225eeec 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -108,6 +108,7 @@
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.function.Consumer;
@@ -2715,6 +2716,9 @@
// Separate position and size for use in animators.
final Rect screenBounds = getAnimationBounds(appRootTaskClipMode);
mTmpRect.set(screenBounds);
+ if (this.asTask() != null && isTaskTransitOld(transit)) {
+ this.asTask().adjustAnimationBoundsForTransition(mTmpRect);
+ }
getAnimationPosition(mTmpPoint);
mTmpRect.offsetTo(0, 0);
@@ -2810,6 +2814,11 @@
if (isTaskTransitOld(transit)) {
animationRunnerBuilder.setTaskBackgroundColor(getTaskAnimationBackgroundColor());
+ // TODO: Remove when we migrate to shell (b/202383002)
+ if (mWmService.mTaskTransitionSpec != null) {
+ animationRunnerBuilder.hideInsetSourceViewOverflows(
+ mWmService.mTaskTransitionSpec.animationBoundInsets);
+ }
}
animationRunnerBuilder.build()
@@ -3554,6 +3563,26 @@
}
}
+ private void hideInsetSourceViewOverflows(Set<Integer> insetTypes) {
+ final ArrayList<SurfaceControl> surfaceControls =
+ new ArrayList<>(insetTypes.size());
+
+ for (int insetType : insetTypes) {
+ InsetsSourceProvider insetProvider = getDisplayContent().getInsetsStateController()
+ .getSourceProvider(insetType);
+
+ // Will apply it immediately to current leash and to all future inset animations
+ // until we disable it.
+ insetProvider.setCropToProvidingInsetsBounds(getPendingTransaction());
+
+ // Only clear the size restriction of the inset once the surface animation is over
+ // and not if it's canceled to be replace by another animation.
+ mOnAnimationFinished.add(() -> {
+ insetProvider.removeCropToProvidingInsetsBounds(getPendingTransaction());
+ });
+ }
+ }
+
private IAnimationStarter build() {
return (Transaction t, AnimationAdapter adapter, boolean hidden,
@AnimationType int type, @Nullable AnimationAdapter snapshotAnim) -> {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 81878e3..1cfbe07 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1110,7 +1110,7 @@
void updateProcessInfo(boolean updateServiceConnectionActivities, boolean activityChange,
boolean updateOomAdj, boolean addPendingTopUid) {
if (addPendingTopUid) {
- mAtm.mAmInternal.addPendingTopUid(mUid, mPid);
+ addToPendingTop();
}
if (updateOomAdj) {
prepareOomAdjustment();
@@ -1121,6 +1121,11 @@
mAtm.mH.sendMessage(m);
}
+ /** Makes the process have top state before oom-adj is computed from a posted message. */
+ void addToPendingTop() {
+ mAtm.mAmInternal.addPendingTopUid(mUid, mPid);
+ }
+
void updateServiceConnectionActivities() {
// Posting on handler so WM lock isn't held when we call into AM.
mAtm.mH.sendMessage(PooledLambda.obtainMessage(
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index b72f05f..663bb2b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -454,66 +454,6 @@
}
@Test
- public void getSessionIdByPackageName() throws Exception {
- FakeStagedSession session = new FakeStagedSession(239);
- session.setCommitted(true);
- session.setSessionReady();
- session.setPackageName("com.foo");
-
- mStagingManager.createSession(session);
- assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(239);
- }
-
- @Test
- public void getSessionIdByPackageName_appliedSession_ignores() throws Exception {
- FakeStagedSession session = new FakeStagedSession(37);
- session.setCommitted(true);
- session.setSessionApplied();
- session.setPackageName("com.foo");
-
- mStagingManager.createSession(session);
- assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(-1);
- }
-
- @Test
- public void getSessionIdByPackageName_failedSession_ignores() throws Exception {
- FakeStagedSession session = new FakeStagedSession(73);
- session.setCommitted(true);
- session.setSessionFailed(1, "whatevs");
- session.setPackageName("com.foo");
-
- mStagingManager.createSession(session);
- assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(-1);
- }
-
- @Test
- public void getSessionIdByPackageName_destroyedSession_ignores() throws Exception {
- FakeStagedSession session = new FakeStagedSession(23);
- session.setCommitted(true);
- session.setDestroyed(true);
- session.setPackageName("com.foo");
-
- mStagingManager.createSession(session);
- assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(-1);
- }
-
- @Test
- public void getSessionIdByPackageName_noSessions() throws Exception {
- assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(-1);
- }
-
- @Test
- public void getSessionIdByPackageName_noSessionHasThisPackage() throws Exception {
- FakeStagedSession session = new FakeStagedSession(37);
- session.setCommitted(true);
- session.setSessionApplied();
- session.setPackageName("com.foo");
-
- mStagingManager.createSession(session);
- assertThat(mStagingManager.getSessionIdByPackageName("com.bar")).isEqualTo(-1);
- }
-
- @Test
public void getStagedApexInfos_validatePreConditions() throws Exception {
// Invalid session: null session
{
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index b0fb812..abd43a4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -623,7 +623,7 @@
wpcAfterConfigChange1.getConfiguration().getLocales());
assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
- mAtm.mInternal.onPackageUninstalled(DEFAULT_PACKAGE_NAME);
+ mAtm.mInternal.onPackageUninstalled(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
WindowProcessController wpcAfterConfigChange2 = createWindowProcessController(
DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index a34586b..66da2a6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -19,6 +19,7 @@
import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -36,6 +37,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
@@ -266,6 +268,30 @@
}
/**
+ * Verifies that process state will be updated with pending top without activity state change.
+ * E.g. switch focus between resumed activities in multi-window mode.
+ */
+ @Test
+ public void testUpdatePendingTopForTopResumed() {
+ final Task task1 = new TaskBuilder(mSupervisor)
+ .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW).build();
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm)
+ .setTask(task1).setUid(ActivityBuilder.DEFAULT_FAKE_UID + 1).build();
+ task1.setResumedActivity(activity1, "test");
+
+ final ActivityRecord activity2 = new TaskBuilder(mSupervisor)
+ .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
+ .setCreateActivity(true).build().getTopMostActivity();
+ activity2.getTask().setResumedActivity(activity2, "test");
+
+ mAtm.mAmInternal.deletePendingTopUid(activity1.getUid());
+ clearInvocations(mAtm);
+ activity1.moveFocusableActivityToTop("test");
+ assertTrue(mAtm.mAmInternal.isPendingTopUid(activity1.getUid()));
+ verify(mAtm).updateOomAdj();
+ }
+
+ /**
* We need to launch home again after user unlocked for those displays that do not have
* encryption aware home app.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index fcd9b3e..af45b80 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -34,13 +34,8 @@
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
-import static android.view.WindowManagerPolicyConstants.ALT_BAR_BOTTOM;
-import static android.view.WindowManagerPolicyConstants.ALT_BAR_LEFT;
-import static android.view.WindowManagerPolicyConstants.ALT_BAR_RIGHT;
-import static android.view.WindowManagerPolicyConstants.ALT_BAR_TOP;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -60,7 +55,6 @@
import android.util.Pair;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
-import android.view.Gravity;
import android.view.InsetsState;
import android.view.InsetsVisibilities;
import android.view.PrivacyIndicatorBounds;
@@ -238,47 +232,6 @@
}
@Test
- public void addingWindow_variousGravities_alternateBarPosUpdated() {
- mDisplayPolicy.removeWindowLw(mNavBarWindow); // Removes the existing one.
-
- WindowState win1 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel1");
- win1.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
- win1.mAttrs.gravity = Gravity.TOP;
- win1.getFrame().set(0, 0, 200, 500);
- addWindow(win1);
-
- assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_TOP);
- mDisplayPolicy.removeWindowLw(win1);
-
- WindowState win2 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel2");
- win2.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
- win2.mAttrs.gravity = Gravity.BOTTOM;
- win2.getFrame().set(0, 0, 200, 500);
- addWindow(win2);
-
- assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_BOTTOM);
- mDisplayPolicy.removeWindowLw(win2);
-
- WindowState win3 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel3");
- win3.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
- win3.mAttrs.gravity = Gravity.LEFT;
- win3.getFrame().set(0, 0, 200, 500);
- addWindow(win3);
-
- assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_LEFT);
- mDisplayPolicy.removeWindowLw(win3);
-
- WindowState win4 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel4");
- win4.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
- win4.mAttrs.gravity = Gravity.RIGHT;
- win4.getFrame().set(0, 0, 200, 500);
- addWindow(win4);
-
- assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_RIGHT);
- mDisplayPolicy.removeWindowLw(win4);
- }
-
- @Test
public void layoutWindowLw_fitStatusBars() {
mWindow.mAttrs.setFitInsetsTypes(Type.statusBars());
addWindowWithRawInsetsState(mWindow);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index a1e8ca4..32cca47 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -37,6 +37,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
@@ -44,6 +45,7 @@
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Intent;
+import android.content.pm.ShortcutServiceInternal;
import android.graphics.PixelFormat;
import android.os.Binder;
import android.os.IBinder;
@@ -71,6 +73,7 @@
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import java.util.ArrayList;
@@ -89,6 +92,7 @@
public class DragDropControllerTests extends WindowTestsBase {
private static final int TIMEOUT_MS = 3000;
private static final int TEST_UID = 12345;
+ private static final int TEST_PROFILE_UID = 12345 * UserHandle.PER_USER_RANGE;
private static final int TEST_PID = 67890;
private static final String TEST_PACKAGE = "com.test.package";
@@ -387,6 +391,32 @@
}
}
+ @Test
+ public void testValidateProfileAppShortcutArguments_notCallingUid() {
+ doReturn(PERMISSION_GRANTED).when(mWm.mContext)
+ .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS));
+ final Session session = Mockito.spy(new Session(mWm, new IWindowSessionCallback.Stub() {
+ @Override
+ public void onAnimatorScaleChanged(float scale) {}
+ }));
+ final ShortcutServiceInternal shortcutService = mock(ShortcutServiceInternal.class);
+ final Intent[] shortcutIntents = new Intent[1];
+ shortcutIntents[0] = new Intent();
+ doReturn(shortcutIntents).when(shortcutService).createShortcutIntents(anyInt(), any(),
+ any(), any(), anyInt(), anyInt(), anyInt());
+ LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
+ LocalServices.addService(ShortcutServiceInternal.class, shortcutService);
+
+ ArgumentCaptor<Integer> callingUser = ArgumentCaptor.forClass(Integer.class);
+ session.validateAndResolveDragMimeTypeExtras(
+ createClipDataForShortcut("test_package", "test_shortcut_id",
+ mock(UserHandle.class)),
+ TEST_PROFILE_UID, TEST_PID, TEST_PACKAGE);
+ verify(shortcutService).createShortcutIntents(callingUser.capture(), any(),
+ any(), any(), anyInt(), anyInt(), anyInt());
+ assertTrue(callingUser.getValue() == UserHandle.getUserId(TEST_PROFILE_UID));
+ }
+
private ClipData createClipDataForShortcut(String packageName, String shortcutId,
UserHandle user) {
final Intent data = new Intent();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index dfbdede..722aee7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -297,6 +297,7 @@
final WindowState appAboveImeTarget = createWindow("appAboveImeTarget");
mDisplayContent.setImeLayeringTarget(imeAppTarget);
+ mDisplayContent.setImeControlTarget(imeAppTarget);
mDisplayContent.assignChildLayers(mTransaction);
// Ime should be above all app windows except for non-fullscreen app window above it and
@@ -343,6 +344,7 @@
@Test
public void testAssignWindowLayers_ForStatusBarImeTarget() {
mDisplayContent.setImeLayeringTarget(mStatusBarWindow);
+ mDisplayContent.setImeControlTarget(mStatusBarWindow);
mDisplayContent.assignChildLayers(mTransaction);
assertWindowHigher(mImeWindow, mChildAppWindowAbove);
@@ -408,6 +410,7 @@
mAppWindow.mActivityRecord, "imeAppTarget");
mDisplayContent.setImeInputTarget(imeAppTarget);
mDisplayContent.setImeLayeringTarget(imeAppTarget);
+ mDisplayContent.setImeControlTarget(imeAppTarget);
mDisplayContent.updateImeParent();
// Simulate the ime layering target task is animating with recents animation.
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 42d7707..a25abc9 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -119,7 +119,7 @@
throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
}
- return new ImsRcsManager(mContext, subscriptionId, sRcsCache);
+ return new ImsRcsManager(mContext, subscriptionId, sRcsCache, sTelephonyCache);
}
/**
@@ -157,7 +157,7 @@
throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
}
- return new SipDelegateManager(mContext, subscriptionId, sRcsCache);
+ return new SipDelegateManager(mContext, subscriptionId, sRcsCache, sTelephonyCache);
}
private static IImsRcsController getIImsRcsControllerInterface() {
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 36082dc..7a27ed7 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -45,6 +45,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -1482,6 +1483,65 @@
}
}
+ /**
+ * Register a new callback, which is used to notify the registrant of changes to
+ * the state of the underlying IMS service that is attached to telephony to
+ * implement IMS functionality. If the manager is created for
+ * the {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID},
+ * this throws an {@link ImsException}.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE}
+ * or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param executor the Executor that will be used to call the {@link ImsStateCallback}.
+ * @param callback The callback instance being registered.
+ * @throws ImsException in the case that the callback can not be registered.
+ * See {@link ImsException#getCode} for more information on when this is called.
+ */
+ @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
+ public void registerImsStateCallback(@NonNull Executor executor,
+ @NonNull ImsStateCallback callback) throws ImsException {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+ Objects.requireNonNull(executor, "Must include a non-null Executor.");
+
+ callback.init(executor);
+ ITelephony telephony = mBinderCache.listenOnBinder(callback, callback::binderDied);
+ if (telephony == null) {
+ throw new ImsException("Telephony server is down",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ telephony.registerImsStateCallback(
+ mSubId, ImsFeature.FEATURE_MMTEL, callback.getCallbackBinder());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Unregisters a previously registered callback.
+ *
+ * @param callback The callback instance to be unregistered.
+ */
+ public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+
+ ITelephony telephony = mBinderCache.removeRunnable(callback);
+ try {
+ if (telephony != null) {
+ telephony.unregisterImsStateCallback(callback.getCallbackBinder());
+ }
+ } catch (RemoteException ignore) {
+ // ignore it
+ }
+ }
+
private ITelephony getITelephony() {
return mBinderCache.getBinder();
}
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 8d6fa41..42af025 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -39,9 +39,11 @@
import android.util.Log;
import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.ITelephony;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -159,6 +161,7 @@
private final int mSubId;
private final Context mContext;
private final BinderCacheManager<IImsRcsController> mBinderCache;
+ private final BinderCacheManager<ITelephony> mTelephonyBinderCache;
private final Map<OnAvailabilityChangedListener, AvailabilityCallbackAdapter>
mAvailabilityChangedCallbacks;
@@ -167,11 +170,13 @@
* @hide
*/
public ImsRcsManager(Context context, int subId,
- BinderCacheManager<IImsRcsController> binderCache) {
+ BinderCacheManager<IImsRcsController> binderCache,
+ BinderCacheManager<ITelephony> telephonyBinderCache) {
mSubId = subId;
mContext = context;
mBinderCache = binderCache;
mAvailabilityChangedCallbacks = new HashMap<>();
+ mTelephonyBinderCache = telephonyBinderCache;
}
/**
@@ -534,6 +539,66 @@
}
/**
+ * Register a new callback, which is used to notify the registrant of changes to
+ * the state of the underlying IMS service that is attached to telephony to
+ * implement IMS functionality. If the manager is created for
+ * the {@link android.telephony.SubscriptionManager#DEFAULT_SUBSCRIPTION_ID},
+ * this throws an {@link ImsException}.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE}
+ * or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param executor the Executor that will be used to call the {@link ImsStateCallback}.
+ * @param callback The callback instance being registered.
+ * @throws ImsException in the case that the callback can not be registered.
+ * See {@link ImsException#getCode} for more information on when this is called.
+ */
+ @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE})
+ public void registerImsStateCallback(@NonNull Executor executor,
+ @NonNull ImsStateCallback callback) throws ImsException {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+ Objects.requireNonNull(executor, "Must include a non-null Executor.");
+
+ callback.init(executor);
+ ITelephony telephony = mTelephonyBinderCache.listenOnBinder(callback, callback::binderDied);
+ if (telephony == null) {
+ throw new ImsException("Telephony server is down",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ telephony.registerImsStateCallback(
+ mSubId, ImsFeature.FEATURE_RCS, callback.getCallbackBinder());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Unregisters a previously registered callback.
+ *
+ * @param callback The callback instance to be unregistered.
+ */
+ public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+
+ ITelephony telephony = mTelephonyBinderCache.removeRunnable(callback);
+ try {
+ if (telephony != null) {
+ telephony.unregisterImsStateCallback(callback.getCallbackBinder());
+ }
+ } catch (RemoteException ignore) {
+ // ignore it
+ }
+ }
+
+ /**
* Add the {@link OnAvailabilityChangedListener} to collection for tracking.
* @param executor The executor that will be used when the publish state is changed and the
* {@link OnAvailabilityChangedListener} is called.
diff --git a/telephony/java/android/telephony/ims/ImsStateCallback.java b/telephony/java/android/telephony/ims/ImsStateCallback.java
new file mode 100644
index 0000000..b9ba93f
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsStateCallback.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Binder;
+
+import com.android.internal.telephony.IImsStateCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.concurrent.Executor;
+
+/**
+ * A callback class used for monitoring changes in IMS service connection states
+ * for a specific subscription.
+ * <p>
+ * @see ImsMmTelManager#registerImsStateCallback(Executor, ImsStateCallback)
+ * @see ImsRcsManager#registerImsStateCallback(Executor, ImsStateCallback)
+ */
+public abstract class ImsStateCallback {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "REASON_", value = {
+ REASON_UNKNOWN_TEMPORARY_ERROR,
+ REASON_UNKNOWN_PERMANENT_ERROR,
+ REASON_IMS_SERVICE_DISCONNECTED,
+ REASON_NO_IMS_SERVICE_CONFIGURED,
+ REASON_SUBSCRIPTION_INACTIVE,
+ REASON_IMS_SERVICE_NOT_READY
+ })
+ public @interface DisconnectedReason {}
+
+ /**
+ * The underlying IMS service is temporarily unavailable for the
+ * associated subscription.
+ * {@link #onAvailable} will be called when the IMS service becomes
+ * available again.
+ */
+ public static final int REASON_UNKNOWN_TEMPORARY_ERROR = 1;
+
+ /**
+ * The underlying IMS service is permanently unavailable for the
+ * associated subscription and there will be no Manager available for
+ * this subscription.
+ */
+ public static final int REASON_UNKNOWN_PERMANENT_ERROR = 2;
+
+ /**
+ * The underlying IMS service has died, is reconfiguring, or has never
+ * come up yet and as a result is currently unavailable.
+ * {@link #onAvailable} will be called when the IMS service becomes
+ * available. All callbacks should be unregistered now and registered again
+ * if the IMS service moves back to available.
+ */
+ public static final int REASON_IMS_SERVICE_DISCONNECTED = 3;
+
+ /**
+ * There is no IMS service configured for the subscription ID specified.
+ * This is a permanent error and there will be no Manager available for
+ * this subscription.
+ */
+ public static final int REASON_NO_IMS_SERVICE_CONFIGURED = 4;
+
+ /**
+ * The subscription associated with this Manager has moved to an inactive
+ * state (e.g. SIM removed) and the IMS service has torn down the resources
+ * related to this subscription. This has caused this callback
+ * to be deregistered. The callback must be re-registered when this subscription
+ * becomes active in order to continue listening to the IMS service state.
+ */
+ public static final int REASON_SUBSCRIPTION_INACTIVE = 5;
+
+ /**
+ * The IMS service is connected, but in a NOT_READY state. Once the
+ * service moves to ready, {@link #onAvailable} will be called.
+ */
+ public static final int REASON_IMS_SERVICE_NOT_READY = 6;
+
+ private IImsStateCallbackStub mCallback;
+
+ /**
+ * @hide
+ */
+ public void init(@NonNull @CallbackExecutor Executor executor) {
+ if (executor == null) {
+ throw new IllegalArgumentException("ImsStateCallback Executor must be non-null");
+ }
+ mCallback = new IImsStateCallbackStub(this, executor);
+ }
+
+ /**
+ * Using a static class and weak reference here to avoid memory leak caused by the
+ * IImsStateCallback.Stub callback retaining references to the outside ImsStateCallback.
+ */
+ private static class IImsStateCallbackStub extends IImsStateCallback.Stub {
+ private WeakReference<ImsStateCallback> mImsStateCallbackWeakRef;
+ private Executor mExecutor;
+
+ IImsStateCallbackStub(ImsStateCallback imsStateCallback, Executor executor) {
+ mImsStateCallbackWeakRef = new WeakReference<ImsStateCallback>(imsStateCallback);
+ mExecutor = executor;
+ }
+
+ Executor getExecutor() {
+ return mExecutor;
+ }
+
+ public void onAvailable() {
+ ImsStateCallback callback = mImsStateCallbackWeakRef.get();
+ if (callback == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> callback.onAvailable()));
+ }
+
+ public void onUnavailable(int reason) {
+ ImsStateCallback callback = mImsStateCallbackWeakRef.get();
+ if (callback == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> callback.onUnavailable(reason)));
+ }
+ }
+
+ /**
+ * The IMS service has disconnected or is reporting NOT_READY and is no longer
+ * available to users. The user should clean up all related state and
+ * unregister callbacks. If it is a temporary error, {@link #onAvailable} will
+ * be called when the IMS service becomes available again.
+ *
+ * @param reason the specified reason
+ */
+ public abstract void onUnavailable(@DisconnectedReason int reason);
+
+ /**
+ * The IMS service is connected and is ready for communication over the
+ * provided Manager.
+ */
+ public abstract void onAvailable();
+
+ /**
+ * An unexpected error has occurred and the Telephony process has crashed. This
+ * has caused this callback to be deregistered. The callback must be
+ * re-registered in order to continue listening to the IMS service state.
+ */
+ public abstract void onError();
+
+ /**
+ * The callback to notify the death of telephony process
+ * @hide
+ */
+ public final void binderDied() {
+ if (mCallback != null) {
+ mCallback.getExecutor().execute(() -> onError());
+ }
+ }
+
+ /**
+ * Return the callback binder
+ * @hide
+ */
+ public IImsStateCallbackStub getCallbackBinder() {
+ return mCallback;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 5a80663..48e3d45 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -28,15 +28,16 @@
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.telephony.BinderCacheManager;
-import android.telephony.CarrierConfigManager;
import android.telephony.ims.aidl.IImsRcsController;
import android.telephony.ims.aidl.SipDelegateConnectionAidlWrapper;
+import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.stub.DelegateConnectionMessageCallback;
import android.telephony.ims.stub.DelegateConnectionStateCallback;
import android.telephony.ims.stub.SipDelegate;
import android.util.ArrayMap;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.ITelephony;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -282,6 +283,7 @@
private final Context mContext;
private final int mSubId;
private final BinderCacheManager<IImsRcsController> mBinderCache;
+ private final BinderCacheManager<ITelephony> mTelephonyBinderCache;
/**
* Only visible for testing. To instantiate an instance of this class, please use
@@ -290,10 +292,12 @@
*/
@VisibleForTesting
public SipDelegateManager(Context context, int subId,
- BinderCacheManager<IImsRcsController> binderCache) {
+ BinderCacheManager<IImsRcsController> binderCache,
+ BinderCacheManager<ITelephony> telephonyBinderCache) {
mContext = context;
mSubId = subId;
mBinderCache = binderCache;
+ mTelephonyBinderCache = telephonyBinderCache;
}
/**
@@ -446,4 +450,64 @@
+ " into this method");
}
}
+
+ /**
+ * Register a new callback, which is used to notify the registrant of changes to
+ * the state of the underlying IMS service that is attached to telephony to
+ * implement IMS functionality. If the manager is created for
+ * the {@link android.telephony.SubscriptionManager#DEFAULT_SUBSCRIPTION_ID},
+ * this throws an {@link ImsException}.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE}
+ * or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param executor the Executor that will be used to call the {@link ImsStateCallback}.
+ * @param callback The callback instance being registered.
+ * @throws ImsException in the case that the callback can not be registered.
+ * See {@link ImsException#getCode} for more information on when this is called.
+ */
+ @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
+ public void registerImsStateCallback(@NonNull Executor executor,
+ @NonNull ImsStateCallback callback) throws ImsException {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+ Objects.requireNonNull(executor, "Must include a non-null Executor.");
+
+ callback.init(executor);
+ ITelephony telephony = mTelephonyBinderCache.listenOnBinder(callback, callback::binderDied);
+ if (telephony == null) {
+ throw new ImsException("Telephony server is down",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ telephony.registerImsStateCallback(
+ mSubId, ImsFeature.FEATURE_RCS, callback.getCallbackBinder());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Unregisters a previously registered callback.
+ *
+ * @param callback The callback instance to be unregistered.
+ */
+ public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+
+ ITelephony telephony = mTelephonyBinderCache.removeRunnable(callback);
+
+ try {
+ if (telephony != null) {
+ telephony.unregisterImsStateCallback(callback.getCallbackBinder());
+ }
+ } catch (RemoteException ignore) {
+ // ignore it
+ }
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl b/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl
new file mode 100644
index 0000000..e04b01d
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+oneway interface IImsStateCallback {
+ void onUnavailable(int reason);
+ void onAvailable();
+}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index d99fb85..6765576 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -67,6 +67,7 @@
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.IBooleanConsumer;
import com.android.internal.telephony.ICallForwardingInfoCallback;
+import com.android.internal.telephony.IImsStateCallback;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.INumberVerificationCallback;
import com.android.internal.telephony.OperatorInfo;
@@ -2497,4 +2498,14 @@
* NSSAIs (configured, allowed and rejected).
*/
void getSlicingConfig(in ResultReceiver callback);
+
+ /**
+ * Register an IMS connection state callback
+ */
+ void registerImsStateCallback(int subId, int feature, in IImsStateCallback cb);
+
+ /**
+ * Unregister an IMS connection state callback
+ */
+ void unregisterImsStateCallback(in IImsStateCallback cb);
}
diff --git a/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h
index 857b25e..f57c4ee 100644
--- a/tools/aapt/SdkConstants.h
+++ b/tools/aapt/SdkConstants.h
@@ -47,6 +47,7 @@
SDK_Q = 29,
SDK_R = 30,
SDK_S = 31,
+ SDK_S_V2 = 32,
SDK_CUR_DEVELOPMENT = 10000,
};
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 0bbde62..8ea43abf 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -59,6 +59,7 @@
{0x0606, SDK_Q},
{0x0616, SDK_R},
{0x064b, SDK_S},
+ {0x064c, SDK_S_V2},
};
static bool less_entry_id(const std::pair<uint16_t, ApiVersion>& p, uint16_t entryId) {
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 384346b..f2aaaf5 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -57,6 +57,7 @@
SDK_Q = 29,
SDK_R = 30,
SDK_S = 31,
+ SDK_S_V2 = 32,
SDK_CUR_DEVELOPMENT = 10000,
};