Merge "ImageUtils: Fix estimated bytes factor for P010"
diff --git a/BROADCASTS_OWNERS b/BROADCASTS_OWNERS
new file mode 100644
index 0000000..1ea610b
--- /dev/null
+++ b/BROADCASTS_OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 316181
+ctate@android.com
+jsharkey@android.com
+jsharkey@google.com
+sudheersai@google.com
+yamasani@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/core/api/current.txt b/core/api/current.txt
index c9b5af5..ff9c7b1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -11938,6 +11938,7 @@
field public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE_DIRECT_ACCESS = "android.hardware.identity_credential_direct_access";
field public static final String FEATURE_INPUT_METHODS = "android.software.input_methods";
field public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
+ field public static final String FEATURE_IPSEC_TUNNEL_MIGRATION = "android.software.ipsec_tunnel_migration";
field public static final String FEATURE_IRIS = "android.hardware.biometrics.iris";
field public static final String FEATURE_KEYSTORE_APP_ATTEST_KEY = "android.hardware.keystore.app_attest_key";
field public static final String FEATURE_KEYSTORE_LIMITED_USE_KEY = "android.hardware.keystore.limited_use_key";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 9ff0afe..6eddc3c0 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -6189,6 +6189,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages();
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method public boolean isAudioServerRunning();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean isBluetoothVariableLatencyEnabled();
method public boolean isHdmiSystemAudioSupported();
method @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public boolean isPstnCallAudioInterceptable();
method @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND) public boolean isUltrasoundSupported();
@@ -6207,6 +6208,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setActiveAssistantServiceUids(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) long);
method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setBluetoothVariableLatencyEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForCapturePreset(int, @NonNull android.media.AudioDeviceAttributes);
@@ -6214,6 +6216,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean supportsBluetoothVariableLatency();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterMuteAwaitConnectionCallback(@NonNull android.media.AudioManager.MuteAwaitConnectionCallback);
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index 1c9713d..2cde539 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -13,3 +13,4 @@
per-file LocusId* = file:/core/java/android/service/contentcapture/OWNERS
per-file ComponentCallbacksController = file:/services/core/java/com/android/server/wm/OWNERS
per-file ComponentCallbacksController = charlesccchen@google.com
+per-file Broadcast* = file:/BROADCASTS_OWNERS
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 7d9ed29..d83ad3d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4058,6 +4058,17 @@
public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+ * the requisite kernel support for migrating IPsec tunnels to new source/destination addresses.
+ *
+ * <p>This feature implies that the device supports XFRM Migration (CONFIG_XFRM_MIGRATE) and has
+ * the kernel fixes to support cross-address-family IPsec tunnel migration
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_IPSEC_TUNNEL_MIGRATION =
+ "android.software.ipsec_tunnel_migration";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports a system interface for the user to select
* and bind device control services provided by applications.
diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS
index 3b6a564..d2a2f12 100644
--- a/core/java/android/hardware/OWNERS
+++ b/core/java/android/hardware/OWNERS
@@ -16,3 +16,6 @@
# Buffers
per-file HardwareBuffer* = file:/graphics/java/android/graphics/OWNERS
per-file DataSpace* = file:/graphics/java/android/graphics/OWNERS
+
+# OverlayProperties
+per-file OverlayProperties* = file:/graphics/java/android/graphics/OWNERS
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 60d8cac..7c2e518 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -108,6 +108,34 @@
}
/**
+ * This is meant to be called by UsbRequest's queue() in order to synchronize on
+ * UsbDeviceConnection's mLock to prevent the connection being closed while queueing.
+ */
+ /* package */ boolean queueRequest(UsbRequest request, ByteBuffer buffer, int length) {
+ synchronized (mLock) {
+ if (!isOpen()) {
+ return false;
+ }
+
+ return request.queueIfConnectionOpen(buffer, length);
+ }
+ }
+
+ /**
+ * This is meant to be called by UsbRequest's queue() in order to synchronize on
+ * UsbDeviceConnection's mLock to prevent the connection being closed while queueing.
+ */
+ /* package */ boolean queueRequest(UsbRequest request, @Nullable ByteBuffer buffer) {
+ synchronized (mLock) {
+ if (!isOpen()) {
+ return false;
+ }
+
+ return request.queueIfConnectionOpen(buffer);
+ }
+ }
+
+ /**
* Releases all system resources related to the device.
* Once the object is closed it cannot be used again.
* The client must call {@link UsbManager#openDevice} again
diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java
index 6ac5e8d..beb0f8d 100644
--- a/core/java/android/hardware/usb/UsbRequest.java
+++ b/core/java/android/hardware/usb/UsbRequest.java
@@ -113,11 +113,13 @@
* Releases all resources related to this request.
*/
public void close() {
- if (mNativeContext != 0) {
- mEndpoint = null;
- mConnection = null;
- native_close();
- mCloseGuard.close();
+ synchronized (mLock) {
+ if (mNativeContext != 0) {
+ mEndpoint = null;
+ mConnection = null;
+ native_close();
+ mCloseGuard.close();
+ }
}
}
@@ -191,10 +193,32 @@
*/
@Deprecated
public boolean queue(ByteBuffer buffer, int length) {
+ UsbDeviceConnection connection = mConnection;
+ if (connection == null) {
+ // The expected exception by CTS Verifier - USB Device test
+ throw new NullPointerException("invalid connection");
+ }
+
+ // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent
+ // the connection being closed while queueing.
+ return connection.queueRequest(this, buffer, length);
+ }
+
+ /**
+ * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over
+ * there, to prevent the connection being closed while queueing.
+ */
+ /* package */ boolean queueIfConnectionOpen(ByteBuffer buffer, int length) {
+ UsbDeviceConnection connection = mConnection;
+ if (connection == null || !connection.isOpen()) {
+ // The expected exception by CTS Verifier - USB Device test
+ throw new NullPointerException("invalid connection");
+ }
+
boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
boolean result;
- if (mConnection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
+ if (connection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
&& length > MAX_USBFS_BUFFER_SIZE) {
length = MAX_USBFS_BUFFER_SIZE;
}
@@ -243,6 +267,28 @@
* @return true if the queueing operation succeeded
*/
public boolean queue(@Nullable ByteBuffer buffer) {
+ UsbDeviceConnection connection = mConnection;
+ if (connection == null) {
+ // The expected exception by CTS Verifier - USB Device test
+ throw new IllegalStateException("invalid connection");
+ }
+
+ // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent
+ // the connection being closed while queueing.
+ return connection.queueRequest(this, buffer);
+ }
+
+ /**
+ * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over
+ * there, to prevent the connection being closed while queueing.
+ */
+ /* package */ boolean queueIfConnectionOpen(@Nullable ByteBuffer buffer) {
+ UsbDeviceConnection connection = mConnection;
+ if (connection == null || !connection.isOpen()) {
+ // The expected exception by CTS Verifier - USB Device test
+ throw new IllegalStateException("invalid connection");
+ }
+
// Request need to be initialized
Preconditions.checkState(mNativeContext != 0, "request is not initialized");
@@ -260,7 +306,7 @@
mIsUsingNewQueue = true;
wasQueued = native_queue(null, 0, 0);
} else {
- if (mConnection.getContext().getApplicationInfo().targetSdkVersion
+ if (connection.getContext().getApplicationInfo().targetSdkVersion
< Build.VERSION_CODES.P) {
// Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once
Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE,
@@ -363,11 +409,12 @@
* @return true if cancelling succeeded
*/
public boolean cancel() {
- if (mConnection == null) {
+ UsbDeviceConnection connection = mConnection;
+ if (connection == null) {
return false;
}
- return mConnection.cancelRequest(this);
+ return connection.cancelRequest(this);
}
/**
@@ -382,7 +429,8 @@
* @return true if cancelling succeeded.
*/
/* package */ boolean cancelIfOpen() {
- if (mNativeContext == 0 || (mConnection != null && !mConnection.isOpen())) {
+ UsbDeviceConnection connection = mConnection;
+ if (mNativeContext == 0 || (connection != null && !connection.isOpen())) {
Log.w(TAG,
"Detected attempt to cancel a request on a connection which isn't open");
return false;
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 935374d1..911aaf3 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -122,7 +122,7 @@
Binder token;
public NfcActivityState(Activity activity) {
- if (activity.getWindow().isDestroyed()) {
+ if (activity.isDestroyed()) {
throw new IllegalStateException("activity is already destroyed");
}
// Check if activity is resumed right now, as we will not
diff --git a/core/java/android/nfc/TechListParcel.java b/core/java/android/nfc/TechListParcel.java
index 90645dd..9f01559 100644
--- a/core/java/android/nfc/TechListParcel.java
+++ b/core/java/android/nfc/TechListParcel.java
@@ -53,7 +53,7 @@
int count = source.readInt();
String[][] techLists = new String[count][];
for (int i = 0; i < count; i++) {
- techLists[i] = source.readStringArray();
+ techLists[i] = source.createStringArray();
}
return new TechListParcel(techLists);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7db6130..b377c92 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3344,9 +3344,26 @@
}
}
- // Fetch all flags for the namespace at once for caching purposes
- Bundle b = cp.call(cr.getAttributionSource(),
- mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
+ Bundle b;
+ // b/252663068: if we're in system server and the caller did not call
+ // clearCallingIdentity, the read would fail due to mismatched AttributionSources.
+ // TODO(b/256013480): remove this bypass after fixing the callers in system server.
+ if (namespace.equals(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER)
+ && Settings.isInSystemServer()
+ && Binder.getCallingUid() != Process.myUid()) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Fetch all flags for the namespace at once for caching purposes
+ b = cp.call(cr.getAttributionSource(),
+ mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } else {
+ // Fetch all flags for the namespace at once for caching purposes
+ b = cp.call(cr.getAttributionSource(),
+ mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
+ }
if (b == null) {
// Invalid response, return an empty map
return keyValues;
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 5ff9263..ac46be7 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4803,6 +4803,13 @@
"phone_number_source_ims";
/**
+ * TelephonyProvider column name for last used TP - message Reference
+ *
+ * @hide
+ */
+ public static final String COLUMN_TP_MESSAGE_REF = "tp_message_ref";
+
+ /**
* TelephonyProvider column name for the device's preferred usage setting.
*
* @hide
diff --git a/core/java/android/service/chooser/OWNERS b/core/java/android/service/chooser/OWNERS
index a5acba5..dcd4a7b 100644
--- a/core/java/android/service/chooser/OWNERS
+++ b/core/java/android/service/chooser/OWNERS
@@ -1,4 +1,3 @@
-asc@google.com
-mpietal@google.com
-dsandler@android.com
-dsandler@google.com
+mrcasey@google.com
+ayepin@google.com
+joshtrask@google.com
diff --git a/core/java/com/android/internal/app/OWNERS b/core/java/com/android/internal/app/OWNERS
index 3833976..970728f 100644
--- a/core/java/com/android/internal/app/OWNERS
+++ b/core/java/com/android/internal/app/OWNERS
@@ -2,6 +2,7 @@
per-file *Resolver* = file:/packages/SystemUI/OWNERS
per-file *Chooser* = file:/packages/SystemUI/OWNERS
per-file SimpleIconFactory.java = file:/packages/SystemUI/OWNERS
+per-file AbstractMultiProfilePagerAdapter.java = file:/packages/SystemUI/OWNERS
per-file NetInitiatedActivity.java = file:/location/java/android/location/OWNERS
per-file *BatteryStats* = file:/BATTERY_STATS_OWNERS
@@ -11,4 +12,4 @@
per-file *Voice* = file:/core/java/android/service/voice/OWNERS
# System language settings
-per-file *Locale* = file:platform/packages/apps/Settings:/src/com/android/settings/localepicker/OWNERS
\ No newline at end of file
+per-file *Locale* = file:platform/packages/apps/Settings:/src/com/android/settings/localepicker/OWNERS
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index 5f4a9cd..473134e 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -172,14 +172,14 @@
@Override
public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
- prepareIntentForCrossProfileLaunch(mResolvedIntent, userId);
+ TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, userId);
activity.startActivityAsCaller(mResolvedIntent, options, false, userId);
return true;
}
@Override
public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
- prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier());
+ TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier());
activity.startActivityAsUser(mResolvedIntent, options, user);
return false;
}
@@ -224,13 +224,6 @@
}
};
- private static void prepareIntentForCrossProfileLaunch(Intent intent, int targetUserId) {
- final int currentUserId = UserHandle.myUserId();
- if (targetUserId != currentUserId) {
- intent.fixUris(currentUserId);
- }
- }
-
private DisplayResolveInfo(Parcel in) {
mDisplayLabel = in.readCharSequence();
mExtendedInfo = in.readCharSequence();
diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
index 264e4f7..4b9b7cb 100644
--- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
@@ -232,6 +232,7 @@
}
intent.setComponent(mChooserTarget.getComponentName());
intent.putExtras(mChooserTarget.getIntentExtras());
+ TargetInfo.prepareIntentForCrossProfileLaunch(intent, userId);
// Important: we will ignore the target security checks in ActivityManager
// if and only if the ChooserTarget's target package is the same package
diff --git a/core/java/com/android/internal/app/chooser/TargetInfo.java b/core/java/com/android/internal/app/chooser/TargetInfo.java
index f56ab17..7bb7ddc 100644
--- a/core/java/com/android/internal/app/chooser/TargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/TargetInfo.java
@@ -130,4 +130,15 @@
* @return true if this target should be pinned to the front by the request of the user
*/
boolean isPinned();
+
+ /**
+ * Fix the URIs in {@code intent} if cross-profile sharing is required. This should be called
+ * before launching the intent as another user.
+ */
+ static void prepareIntentForCrossProfileLaunch(Intent intent, int targetUserId) {
+ final int currentUserId = UserHandle.myUserId();
+ if (targetUserId != currentUserId) {
+ intent.fixUris(currentUserId);
+ }
+ }
}
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 77317d1..47057a4 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2952,6 +2952,30 @@
return jStatus;
}
+static jboolean android_media_AudioSystem_supportsBluetoothVariableLatency(JNIEnv *env,
+ jobject thiz) {
+ bool supports;
+ if (AudioSystem::supportsBluetoothVariableLatency(&supports) != NO_ERROR) {
+ supports = false;
+ }
+ return supports;
+}
+
+static int android_media_AudioSystem_setBluetoothVariableLatencyEnabled(JNIEnv *env, jobject thiz,
+ jboolean enabled) {
+ return (jint)check_AudioSystem_Command(
+ AudioSystem::setBluetoothVariableLatencyEnabled(enabled));
+}
+
+static jboolean android_media_AudioSystem_isBluetoothVariableLatencyEnabled(JNIEnv *env,
+ jobject thiz) {
+ bool enabled;
+ if (AudioSystem::isBluetoothVariableLatencyEnabled(&enabled) != NO_ERROR) {
+ enabled = false;
+ }
+ return enabled;
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] =
@@ -3104,7 +3128,13 @@
(void *)android_media_AudioSystem_getDirectPlaybackSupport},
{"getDirectProfilesForAttributes",
"(Landroid/media/AudioAttributes;Ljava/util/ArrayList;)I",
- (void *)android_media_AudioSystem_getDirectProfilesForAttributes}};
+ (void *)android_media_AudioSystem_getDirectProfilesForAttributes},
+ {"supportsBluetoothVariableLatency", "()Z",
+ (void *)android_media_AudioSystem_supportsBluetoothVariableLatency},
+ {"setBluetoothVariableLatencyEnabled", "(Z)I",
+ (void *)android_media_AudioSystem_setBluetoothVariableLatencyEnabled},
+ {"isBluetoothVariableLatencyEnabled", "()Z",
+ (void *)android_media_AudioSystem_isBluetoothVariableLatencyEnabled}};
static const JNINativeMethod gEventHandlerMethods[] = {
{"native_setup",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e027332..5bcba48 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -257,6 +257,7 @@
android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY" />
<protected-broadcast
android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.action.HAP_CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_CONF_CHANGED" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 855ecfb..73c201c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -136,9 +136,9 @@
<!-- Displayed when a SIM PUK password is too short. -->
<string name="invalidPuk">Type a PUK that is 8 numbers or longer.</string>
<!-- Displayed to prompt the user to type the PUK password to unlock
- the SIM card. -->
- <string name="needPuk">Your SIM card is PUK-locked. Type the PUK code to unlock it.</string>
- <string name="needPuk2">Type PUK2 to unblock SIM card.</string>
+ the SIM. -->
+ <string name="needPuk">Your SIM is PUK-locked. Type the PUK code to unlock it.</string>
+ <string name="needPuk2">Type PUK2 to unblock SIM.</string>
<!-- Displayed when user attempts to change SIM PIN1 without enabling PIN1. -->
<string name="enablePin">Unsuccessful, enable SIM/RUIM Lock.</string>
<!-- Displayed when a SIM PIN/PUK is entered incorrectly. -->
@@ -2434,23 +2434,23 @@
<!-- Shown when face unlock failed multiple times so we're just using the backup -->
<string name="faceunlock_multiple_failures">Maximum Face Unlock attempts exceeded</string>
- <!-- Shown in the lock screen when there is no SIM card. -->
- <string name="lockscreen_missing_sim_message_short">No SIM card</string>
- <!-- Shown in the lock screen when there is no SIM card. -->
- <string name="lockscreen_missing_sim_message" product="tablet">No SIM card in tablet.</string>
- <!-- Shown in the lock screen when there is no SIM card. -->
- <string name="lockscreen_missing_sim_message" product="tv">No SIM card in your Android TV device.</string>
- <!-- Shown in the lock screen when there is no SIM card. -->
- <string name="lockscreen_missing_sim_message" product="default">No SIM card in phone.</string>
- <!-- Shown in the lock screen to ask the user to insert a SIM card. -->
- <string name="lockscreen_missing_sim_instructions">Insert a SIM card.</string>
- <!-- Shown in the lock screen to ask the user to insert a SIM card when sim is missing or not readable. -->
- <string name="lockscreen_missing_sim_instructions_long">The SIM card is missing or not readable. Insert a SIM card.</string>
- <!-- Shown in the lock screen when SIM card is permanently disabled. -->
- <string name="lockscreen_permanent_disabled_sim_message_short">Unusable SIM card.</string>
- <!-- Shown in the lock screen to inform the user to SIM card is permanently disabled. -->
- <string name="lockscreen_permanent_disabled_sim_instructions">Your SIM card has been permanently disabled.\n
- Contact your wireless service provider for another SIM card.</string>
+ <!-- Shown in the lock screen when there is no SIM. -->
+ <string name="lockscreen_missing_sim_message_short">No SIM</string>
+ <!-- Shown in the lock screen when there is no SIM. -->
+ <string name="lockscreen_missing_sim_message" product="tablet">No SIM in tablet.</string>
+ <!-- Shown in the lock screen when there is no SIM. -->
+ <string name="lockscreen_missing_sim_message" product="tv">No SIM in your Android TV device.</string>
+ <!-- Shown in the lock screen when there is no SIM. -->
+ <string name="lockscreen_missing_sim_message" product="default">No SIM in phone.</string>
+ <!-- Shown in the lock screen to ask the user to add a SIM. -->
+ <string name="lockscreen_missing_sim_instructions">Add a SIM.</string>
+ <!-- Shown in the lock screen to ask the user to add a SIM when sim is missing or not readable. -->
+ <string name="lockscreen_missing_sim_instructions_long">The SIM is missing or not readable. Add a SIM.</string>
+ <!-- Shown in the lock screen when SIM is permanently disabled. -->
+ <string name="lockscreen_permanent_disabled_sim_message_short">Unusable SIM.</string>
+ <!-- Shown in the lock screen to inform the user to SIM is permanently deactivated. -->
+ <string name="lockscreen_permanent_disabled_sim_instructions">Your SIM has been permanently deactivated.\n
+ Contact your wireless service provider for another SIM.</string>
<!-- Shown on transport control of lockscreen. Pressing button goes to previous track. -->
<string name="lockscreen_transport_prev_description">Previous track</string>
@@ -2477,17 +2477,17 @@
<!-- When the user enters a wrong sim pin too many times, it becomes
PUK locked (Pin Unlock Kode) -->
- <string name="lockscreen_sim_puk_locked_message">SIM card is PUK-locked.</string>
+ <string name="lockscreen_sim_puk_locked_message">SIM is PUK-locked.</string>
<!-- Shown in the lock screen when the SIM has become PUK locked and the user must call customer care to unlock it. -->
<string name="lockscreen_sim_puk_locked_instructions">See the User Guide or contact Customer Care.</string>
<!-- Shown in the lock screen to tell the user that their SIM is locked and they must unlock it. -->
- <string name="lockscreen_sim_locked_message">SIM card is locked.</string>
+ <string name="lockscreen_sim_locked_message">SIM is locked.</string>
<!-- For the unlock screen, When the user enters a sim unlock code, it takes a little while to check
whether it is valid, and to unlock the sim if it is valid. we display a
progress dialog in the meantime. this is the emssage. -->
- <string name="lockscreen_sim_unlock_progress_dialog_message">Unlocking SIM card\u2026</string>
+ <string name="lockscreen_sim_unlock_progress_dialog_message">Unlocking SIM\u2026</string>
<!-- For the unlock screen, Information message shown in dialog when user has too many failed attempts at
drawing the unlock pattern -->
@@ -3781,13 +3781,13 @@
<!-- SIM swap and device reboot Dialog --> <skip />
<!-- See SIM_REMOVED_DIALOG. This is the title of that dialog. -->
- <string name="sim_removed_title">SIM card removed</string>
+ <string name="sim_removed_title">SIM removed</string>
<!-- See SIM_REMOVED_DIALOG. This is the message of that dialog. -->
- <string name="sim_removed_message">The mobile network will be unavailable until you restart with a valid SIM card inserted.</string>
+ <string name="sim_removed_message">The mobile network will be unavailable until you restart with a valid SIM.</string>
<!-- See SIM_REMOVED_DIALOG. This is the button of that dialog. -->
<string name="sim_done_button">Done</string>
<!-- See SIM_ADDED_DIALOG. This is the title of that dialog. -->
- <string name="sim_added_title">SIM card added</string>
+ <string name="sim_added_title">SIM added</string>
<!-- See SIM_ADDED_DIALOG. This is the message of that dialog. -->
<string name="sim_added_message">Restart your device to access the mobile network.</string>
<!-- See SIM_ADDED_DIALOG. This is the button of that dialog. -->
@@ -4611,8 +4611,8 @@
<string name="kg_puk_enter_pin_hint">Enter desired PIN code</string>
<!-- Message shown when the user needs to confirm the PIN they just entered in the PUK screen -->
<string name="kg_enter_confirm_pin_hint">Confirm desired PIN code</string>
- <!-- Message shown in dialog while the device is unlocking the SIM card -->
- <string name="kg_sim_unlock_progress_dialog_message">Unlocking SIM card\u2026</string>
+ <!-- Message shown in dialog while the device is unlocking the SIM -->
+ <string name="kg_sim_unlock_progress_dialog_message">Unlocking SIM\u2026</string>
<!-- Message shown when the user enters the wrong PIN code -->
<string name="kg_password_wrong_pin_code">Incorrect PIN code.</string>
<!-- Message shown when the user enters an invalid SIM pin password in PUK screen -->
diff --git a/core/tests/coretests/src/android/companion/virtual/OWNERS b/core/tests/coretests/src/android/companion/virtual/OWNERS
index 1a3e927..2e475a9 100644
--- a/core/tests/coretests/src/android/companion/virtual/OWNERS
+++ b/core/tests/coretests/src/android/companion/virtual/OWNERS
@@ -1,3 +1 @@
-set noparent
-
include /services/companion/java/com/android/server/companion/virtual/OWNERS
\ No newline at end of file
diff --git a/core/tests/nfctests/Android.bp b/core/tests/nfctests/Android.bp
index 335cea1..c74600b 100644
--- a/core/tests/nfctests/Android.bp
+++ b/core/tests/nfctests/Android.bp
@@ -27,6 +27,7 @@
"androidx.test.ext.junit",
"androidx.test.rules",
"mockito-target-minus-junit4",
+ "truth-prebuilt",
],
libs: [
"android.test.runner",
diff --git a/core/tests/nfctests/src/android/nfc/TechListParcelTest.java b/core/tests/nfctests/src/android/nfc/TechListParcelTest.java
new file mode 100644
index 0000000..a12bbbc
--- /dev/null
+++ b/core/tests/nfctests/src/android/nfc/TechListParcelTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2022 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.nfc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public class TechListParcelTest {
+
+ private static final String[] TECH_LIST_1 = new String[] { "tech1.1", "tech1.2" };
+ private static final String[] TECH_LIST_2 = new String[] { "tech2.1" };
+ private static final String[] TECH_LIST_EMPTY = new String[] {};
+
+ @Test
+ public void testWriteParcel() {
+ TechListParcel techListParcel = new TechListParcel(TECH_LIST_1, TECH_LIST_2);
+
+ Parcel parcel = Parcel.obtain();
+ techListParcel.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ TechListParcel actualTechList =
+ TechListParcel.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ assertThat(actualTechList.getTechLists().length).isEqualTo(2);
+ assertThat(Arrays.equals(actualTechList.getTechLists()[0], TECH_LIST_1)).isTrue();
+ assertThat(Arrays.equals(actualTechList.getTechLists()[1], TECH_LIST_2)).isTrue();
+ }
+
+ @Test
+ public void testWriteParcelArrayEmpty() {
+ TechListParcel techListParcel = new TechListParcel();
+
+ Parcel parcel = Parcel.obtain();
+ techListParcel.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ TechListParcel actualTechList =
+ TechListParcel.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ assertThat(actualTechList.getTechLists().length).isEqualTo(0);
+ }
+
+ @Test
+ public void testWriteParcelElementEmpty() {
+ TechListParcel techListParcel = new TechListParcel(TECH_LIST_EMPTY);
+
+ Parcel parcel = Parcel.obtain();
+ techListParcel.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ TechListParcel actualTechList =
+ TechListParcel.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ assertThat(actualTechList.getTechLists().length).isEqualTo(1);
+ assertThat(Arrays.equals(actualTechList.getTechLists()[0], TECH_LIST_EMPTY)).isTrue();
+ }
+
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 8ca1607..ff42fb5 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -342,10 +342,6 @@
<permission name="android.permission.SET_WALLPAPER_COMPONENT" />
<permission name="android.permission.SET_WALLPAPER_DIM_AMOUNT" />
<permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
- <!-- Permission required for CTS test - TrustTestCases -->
- <permission name="android.permission.PROVIDE_TRUST_AGENT" />
- <permission name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
- <permission name="android.permission.TRUST_LISTENER" />
<!-- Permissions required for Incremental CTS tests -->
<permission name="com.android.permission.USE_INSTALLER_V2"/>
<permission name="android.permission.LOADER_USAGE_STATS"/>
diff --git a/data/keyboards/Vendor_054c_Product_0df2.kl b/data/keyboards/Vendor_054c_Product_0df2.kl
new file mode 100644
index 0000000..a47b310
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0df2.kl
@@ -0,0 +1,73 @@
+# Copyright (C) 2022 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.
+
+#
+# Sony Playstation(R) DualSense Edge Controller
+#
+
+# Only use this key layout if we have HID_PLAYSTATION!
+requires_kernel_config CONFIG_HID_PLAYSTATION
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134 BUTTON_X
+# Cross
+key 0x130 BUTTON_A
+# Circle
+key 0x131 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+# L2 axis
+axis 0x02 LTRIGGER
+# R2 axis
+axis 0x05 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x03 Z
+axis 0x04 RZ
+
+# Left stick click
+key 0x13d BUTTON_THUMBL
+# Right stick click
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share / "half-sun"
+key 0x13a BUTTON_SELECT
+# Options / three horizontal lines
+key 0x13b BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# SENSORs
+sensor 0x00 ACCELEROMETER X
+sensor 0x01 ACCELEROMETER Y
+sensor 0x02 ACCELEROMETER Z
+sensor 0x03 GYROSCOPE X
+sensor 0x04 GYROSCOPE Y
+sensor 0x05 GYROSCOPE Z
diff --git a/data/keyboards/Vendor_054c_Product_0df2_fallback.kl b/data/keyboards/Vendor_054c_Product_0df2_fallback.kl
new file mode 100644
index 0000000..bfebb17
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0df2_fallback.kl
@@ -0,0 +1,75 @@
+# Copyright (C) 2022 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.
+
+#
+# Sony Playstation(R) DualSense Edge Controller
+#
+
+# Use this if HID_PLAYSTATION is not available
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 304 BUTTON_X
+# Cross
+key 305 BUTTON_A
+# Circle
+key 306 BUTTON_B
+# Triangle
+key 307 BUTTON_Y
+
+key 308 BUTTON_L1
+key 309 BUTTON_R1
+key 310 BUTTON_L2
+key 311 BUTTON_R2
+
+# L2 axis
+axis 0x03 LTRIGGER
+# R2 axis
+axis 0x04 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# Left stick click
+key 314 BUTTON_THUMBL
+# Right stick click
+key 315 BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share / "half-sun"
+key 312 BUTTON_SELECT
+# Options / three horizontal lines
+key 313 BUTTON_START
+# PS key
+key 316 BUTTON_MODE
+
+# Touchpad press
+key 317 BUTTON_1
+
+# SENSORs
+sensor 0x00 ACCELEROMETER X
+sensor 0x01 ACCELEROMETER Y
+sensor 0x02 ACCELEROMETER Z
+sensor 0x03 GYROSCOPE X
+sensor 0x04 GYROSCOPE Y
+sensor 0x05 GYROSCOPE Z
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 9947d34..c55a781 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -38,6 +38,7 @@
import java.security.Security;
import java.security.Signature;
import java.security.UnrecoverableKeyException;
+import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
@@ -221,7 +222,14 @@
}
final byte[] x509PublicCert = metadata.certificate;
- PublicKey publicKey = AndroidKeyStoreSpi.toCertificate(x509PublicCert).getPublicKey();
+ final X509Certificate parsedX509Certificate =
+ AndroidKeyStoreSpi.toCertificate(x509PublicCert);
+ if (parsedX509Certificate == null) {
+ throw new UnrecoverableKeyException("Failed to parse the X.509 certificate containing"
+ + " the public key. This likely indicates a hardware problem.");
+ }
+
+ PublicKey publicKey = parsedX509Certificate.getPublicKey();
String jcaKeyAlgorithm = publicKey.getAlgorithm();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 399650d..85911a3 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -8478,6 +8478,55 @@
}
}
+ /**
+ * Requests if the implementation supports controlling the latency modes
+ * over the Bluetooth A2DP or LE Audio links.
+ *
+ * @return true if supported, false otherwise
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public boolean supportsBluetoothVariableLatency() {
+ try {
+ return getService().supportsBluetoothVariableLatency();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Enables or disables the variable Bluetooth latency control mechanism in the
+ * audio framework and the audio HAL. This does not apply to the latency mode control
+ * on the spatializer output as this is a built-in feature.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public void setBluetoothVariableLatencyEnabled(boolean enabled) {
+ try {
+ getService().setBluetoothVariableLatencyEnabled(enabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Indicates if the variable Bluetooth latency control mechanism is enabled or disabled.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public boolean isBluetoothVariableLatencyEnabled() {
+ try {
+ return getService().isBluetoothVariableLatencyEnabled();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private final Object mMuteAwaitConnectionListenerLock = new Object();
@GuardedBy("mMuteAwaitConnectionListenerLock")
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 097bb41..474ccc9 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -2428,4 +2428,30 @@
* Keep in sync with core/jni/android_media_DeviceCallback.h.
*/
final static int NATIVE_EVENT_ROUTING_CHANGE = 1000;
+
+
+ /**
+ * Requests if the implementation supports controlling the latency modes
+ * over the Bluetooth A2DP or LE Audio links.
+ *
+ * @return true if supported, false otherwise
+ *
+ * @hide
+ */
+ public static native boolean supportsBluetoothVariableLatency();
+
+ /**
+ * Enables or disables the variable Bluetooth latency control mechanism in the
+ * audio framework and the audio HAL. This does not apply to the latency mode control
+ * on the spatializer output as this is a built-in feature.
+ *
+ * @hide
+ */
+ public static native int setBluetoothVariableLatencyEnabled(boolean enabled);
+
+ /**
+ * Indicates if the variable Bluetooth latency control mechanism is enabled or disabled.
+ * @hide
+ */
+ public static native boolean isBluetoothVariableLatencyEnabled();
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index cfcd79a..9375a0d 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -517,4 +517,16 @@
boolean handlesvolumeAdjustment);
AudioHalVersionInfo getHalVersion();
+
+ @EnforcePermission("MODIFY_AUDIO_ROUTING")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)")
+ boolean supportsBluetoothVariableLatency();
+
+ @EnforcePermission("MODIFY_AUDIO_ROUTING")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)")
+ void setBluetoothVariableLatencyEnabled(boolean enabled);
+
+ @EnforcePermission("MODIFY_AUDIO_ROUTING")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)")
+ boolean isBluetoothVariableLatencyEnabled();
}
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index e60d537..667a9ae 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -946,6 +946,10 @@
id = generateInputId(componentName, mTvInputHardwareInfo);
type = sHardwareTypeToTvInputType.get(mTvInputHardwareInfo.getType(), TYPE_TUNER);
isHardwareInput = true;
+ if (mTvInputHardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
+ mHdmiDeviceInfo = HdmiDeviceInfo.hardwarePort(
+ HdmiDeviceInfo.PATH_INVALID, mTvInputHardwareInfo.getHdmiPortId());
+ }
} else {
id = generateInputId(componentName);
type = TYPE_TUNER;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 5c796af..879181f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -278,6 +278,18 @@
return false;
}
+ /**
+ * Check if a device class matches with a defined BluetoothClass device.
+ *
+ * @param device Must be one of the public constants in {@link BluetoothClass.Device}
+ * @return true if device class matches, false otherwise.
+ */
+ public static boolean isDeviceClassMatched(@NonNull BluetoothDevice bluetoothDevice,
+ int device) {
+ return bluetoothDevice.getBluetoothClass() != null
+ && bluetoothDevice.getBluetoothClass().getDeviceClass() == device;
+ }
+
private static boolean isAdvancedHeaderEnabled() {
if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI, BT_ADVANCED_HEADER_ENABLED,
true)) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index eb53ea1..c8187b8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -35,7 +35,6 @@
import android.os.ParcelUuid;
import android.os.SystemClock;
import android.text.TextUtils;
-import android.util.EventLog;
import android.util.Log;
import android.util.LruCache;
import android.util.Pair;
@@ -1020,15 +1019,7 @@
if (BluetoothUuid.containsAnyUuid(uuids, PbapServerProfile.PBAB_CLIENT_UUIDS)) {
// The pairing dialog now warns of phone-book access for paired devices.
// No separate prompt is displayed after pairing.
- final BluetoothClass bluetoothClass = mDevice.getBluetoothClass();
- if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_UNKNOWN) {
- if (bluetoothClass != null && (bluetoothClass.getDeviceClass()
- == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE
- || bluetoothClass.getDeviceClass()
- == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)) {
- EventLog.writeEvent(0x534e4554, "138529441", -1, "");
- }
- }
+ mDevice.getPhonebookAccessPermission();
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index fd7554f..cd667ca 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -48,6 +48,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -376,9 +377,11 @@
Setting newSetting = new Setting(name, oldSetting.getValue(), null,
oldSetting.getPackageName(), oldSetting.getTag(), false,
oldSetting.getId());
+ int newSize = getNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), 0,
+ oldValue, newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue());
+ checkNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize);
mSettings.put(name, newSetting);
- updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue,
- newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue());
+ updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize);
scheduleWriteIfNeededLocked();
}
}
@@ -410,6 +413,13 @@
Setting oldState = mSettings.get(name);
String oldValue = (oldState != null) ? oldState.value : null;
String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null;
+ String newDefaultValue = makeDefault ? value : oldDefaultValue;
+
+ int newSize = getNewMemoryUsagePerPackageLocked(packageName,
+ oldValue == null ? name.length() : 0 /* deltaKeySize */,
+ oldValue, value, oldDefaultValue, newDefaultValue);
+ checkNewMemoryUsagePerPackageLocked(packageName, newSize);
+
Setting newState;
if (oldState != null) {
@@ -430,8 +440,7 @@
addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState);
- updateMemoryUsagePerPackageLocked(packageName, oldValue, value,
- oldDefaultValue, newState.getDefaultValue());
+ updateMemoryUsagePerPackageLocked(packageName, newSize);
scheduleWriteIfNeededLocked();
@@ -552,13 +561,18 @@
}
Setting oldState = mSettings.remove(name);
+ if (oldState == null) {
+ return false;
+ }
+ int newSize = getNewMemoryUsagePerPackageLocked(oldState.packageName,
+ -name.length() /* deltaKeySize */,
+ oldState.value, null, oldState.defaultValue, null);
FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, /* value= */ "",
/* newValue= */ "", oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED);
- updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value,
- null, oldState.defaultValue, null);
+ updateMemoryUsagePerPackageLocked(oldState.packageName, newSize);
addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState);
@@ -575,20 +589,23 @@
}
Setting setting = mSettings.get(name);
+ if (setting == null) {
+ return false;
+ }
Setting oldSetting = new Setting(setting);
String oldValue = setting.getValue();
String oldDefaultValue = setting.getDefaultValue();
+ int newSize = getNewMemoryUsagePerPackageLocked(setting.packageName, 0, oldValue,
+ oldDefaultValue, oldDefaultValue, oldDefaultValue);
+ checkNewMemoryUsagePerPackageLocked(setting.packageName, newSize);
+
if (!setting.reset()) {
return false;
}
- String newValue = setting.getValue();
- String newDefaultValue = setting.getDefaultValue();
-
- updateMemoryUsagePerPackageLocked(setting.packageName, oldValue,
- newValue, oldDefaultValue, newDefaultValue);
+ updateMemoryUsagePerPackageLocked(setting.packageName, newSize);
addHistoricalOperationLocked(HISTORICAL_OPERATION_RESET, oldSetting);
@@ -696,38 +713,49 @@
}
@GuardedBy("mLock")
- private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue,
- String newValue, String oldDefaultValue, String newDefaultValue) {
- if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) {
+ private boolean isExemptFromMemoryUsageCap(String packageName) {
+ return mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED
+ || SYSTEM_PACKAGE_NAME.equals(packageName);
+ }
+
+ @GuardedBy("mLock")
+ private void checkNewMemoryUsagePerPackageLocked(String packageName, int newSize)
+ throws IllegalStateException {
+ if (isExemptFromMemoryUsageCap(packageName)) {
return;
}
-
- if (SYSTEM_PACKAGE_NAME.equals(packageName)) {
- return;
- }
-
- final int oldValueSize = (oldValue != null) ? oldValue.length() : 0;
- final int newValueSize = (newValue != null) ? newValue.length() : 0;
- final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0;
- final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0;
- final int deltaSize = newValueSize + newDefaultValueSize
- - oldValueSize - oldDefaultValueSize;
-
- Integer currentSize = mPackageToMemoryUsage.get(packageName);
- final int newSize = Math.max((currentSize != null)
- ? currentSize + deltaSize : deltaSize, 0);
-
if (newSize > mMaxBytesPerAppPackage) {
throw new IllegalStateException("You are adding too many system settings. "
+ "You should stop using system settings for app specific data"
+ " package: " + packageName);
}
+ }
+ @GuardedBy("mLock")
+ private int getNewMemoryUsagePerPackageLocked(String packageName, int deltaKeySize,
+ String oldValue, String newValue, String oldDefaultValue, String newDefaultValue) {
+ if (isExemptFromMemoryUsageCap(packageName)) {
+ return 0;
+ }
+ final Integer currentSize = mPackageToMemoryUsage.get(packageName);
+ final int oldValueSize = (oldValue != null) ? oldValue.length() : 0;
+ final int newValueSize = (newValue != null) ? newValue.length() : 0;
+ final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0;
+ final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0;
+ final int deltaSize = deltaKeySize + newValueSize + newDefaultValueSize
+ - oldValueSize - oldDefaultValueSize;
+ return Math.max((currentSize != null) ? currentSize + deltaSize : deltaSize, 0);
+ }
+
+ @GuardedBy("mLock")
+ private void updateMemoryUsagePerPackageLocked(String packageName, int newSize) {
+ if (isExemptFromMemoryUsageCap(packageName)) {
+ return;
+ }
if (DEBUG) {
Slog.i(LOG_TAG, "Settings for package: " + packageName
+ " size: " + newSize + " bytes.");
}
-
mPackageToMemoryUsage.put(packageName, newSize);
}
@@ -1556,4 +1584,11 @@
}
return false;
}
+
+ @VisibleForTesting
+ public int getMemoryUsage(String packageName) {
+ synchronized (mLock) {
+ return mPackageToMemoryUsage.getOrDefault(packageName, 0);
+ }
+ }
}
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index 69eb713..f6d4329 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -20,6 +20,8 @@
import android.util.TypedXmlSerializer;
import android.util.Xml;
+import com.google.common.base.Strings;
+
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
@@ -276,4 +278,132 @@
settingsState.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING);
return settingsState;
}
+
+ public void testInsertSetting_memoryUsage() {
+ SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+ // No exception should be thrown when there is no cap
+ settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
+ null, false, "p1");
+ settingsState.deleteSettingLocked(SETTING_NAME);
+
+ settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper());
+ // System package doesn't have memory usage limit
+ settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
+ null, false, SYSTEM_PACKAGE);
+ settingsState.deleteSettingLocked(SETTING_NAME);
+
+ // Should not throw if usage is under the cap
+ settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 19975),
+ null, false, "p1");
+ settingsState.deleteSettingLocked(SETTING_NAME);
+ try {
+ settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
+ null, false, "p1");
+ fail("Should throw because it exceeded per package memory usage");
+ } catch (IllegalStateException ex) {
+ assertTrue(ex.getMessage().contains("p1"));
+ }
+ try {
+ settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
+ null, false, "p1");
+ fail("Should throw because it exceeded per package memory usage");
+ } catch (IllegalStateException ex) {
+ assertTrue(ex.getMessage().contains("p1"));
+ }
+ assertTrue(settingsState.getSettingLocked(SETTING_NAME).isNull());
+ try {
+ settingsState.insertSettingLocked(Strings.repeat("A", 20001), "",
+ null, false, "p1");
+ fail("Should throw because it exceeded per package memory usage");
+ } catch (IllegalStateException ex) {
+ assertTrue(ex.getMessage().contains("You are adding too many system settings"));
+ }
+ }
+
+ public void testMemoryUsagePerPackage() {
+ SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper());
+
+ // Test inserting one key with default
+ final String testKey1 = SETTING_NAME;
+ final String testValue1 = Strings.repeat("A", 100);
+ settingsState.insertSettingLocked(testKey1, testValue1, null, true, TEST_PACKAGE);
+ int expectedMemUsage = testKey1.length() + testValue1.length()
+ + testValue1.length() /* size for default */;
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test inserting another key
+ final String testKey2 = SETTING_NAME + "2";
+ settingsState.insertSettingLocked(testKey2, testValue1, null, false, TEST_PACKAGE);
+ expectedMemUsage += testKey2.length() + testValue1.length();
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test updating first key with new default
+ final String testValue2 = Strings.repeat("A", 300);
+ settingsState.insertSettingLocked(testKey1, testValue2, null, true, TEST_PACKAGE);
+ expectedMemUsage += (testValue2.length() - testValue1.length()) * 2;
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test updating first key without new default
+ final String testValue3 = Strings.repeat("A", 50);
+ settingsState.insertSettingLocked(testKey1, testValue3, null, false, TEST_PACKAGE);
+ expectedMemUsage -= testValue2.length() - testValue3.length();
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test updating second key
+ settingsState.insertSettingLocked(testKey2, testValue2, null, false, TEST_PACKAGE);
+ expectedMemUsage -= testValue1.length() - testValue2.length();
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test resetting key
+ settingsState.resetSettingLocked(testKey1);
+ expectedMemUsage += testValue2.length() - testValue3.length();
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test resetting default value
+ settingsState.resetSettingDefaultValueLocked(testKey1);
+ expectedMemUsage -= testValue2.length();
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test deletion
+ settingsState.deleteSettingLocked(testKey2);
+ expectedMemUsage -= testValue2.length() + testKey2.length() /* key is deleted too */;
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test another package with a different key
+ final String testPackage2 = TEST_PACKAGE + "2";
+ final String testKey3 = SETTING_NAME + "3";
+ settingsState.insertSettingLocked(testKey3, testValue1, null, true, testPackage2);
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+ final int expectedMemUsage2 = testKey3.length() + testValue1.length() * 2;
+ assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2));
+
+ // Test system package
+ settingsState.insertSettingLocked(testKey1, testValue1, null, true, SYSTEM_PACKAGE);
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+ assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2));
+ assertEquals(0, settingsState.getMemoryUsage(SYSTEM_PACKAGE));
+
+ // Test invalid value
+ try {
+ settingsState.insertSettingLocked(testKey1, Strings.repeat("A", 20001), null, false,
+ TEST_PACKAGE);
+ fail("Should throw because it exceeded per package memory usage");
+ } catch (IllegalStateException ex) {
+ assertTrue(ex.getMessage().contains("You are adding too many system settings"));
+ }
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test invalid key
+ try {
+ settingsState.insertSettingLocked(Strings.repeat("A", 20001), "", null, false,
+ TEST_PACKAGE);
+ fail("Should throw because it exceeded per package memory usage");
+ } catch (IllegalStateException ex) {
+ assertTrue(ex.getMessage().contains("You are adding too many system settings"));
+ }
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+ }
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 979e9ea..f13d961 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -578,11 +578,6 @@
<!-- Permission required for CTS test - PeopleManagerTest -->
<uses-permission android:name="android.permission.READ_PEOPLE_DATA" />
- <!-- Permissions required for CTS test - TrustTestCases -->
- <uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" />
- <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
- <uses-permission android:name="android.permission.TRUST_LISTENER" />
-
<!-- Permission required for CTS test - CtsTaskFpsCallbackTestCases -->
<uses-permission android:name="android.permission.ACCESS_FPS_COUNTER" />
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index d90156d..0a9a93b 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -61,25 +61,25 @@
<!-- SIM messages --><skip />
<!-- When the user inserts a sim card from an unsupported network, it becomes network locked -->
<string name="keyguard_network_locked_message">Network locked</string>
- <!-- Shown when there is no SIM card. -->
- <string name="keyguard_missing_sim_message_short">No SIM card</string>
- <!-- Shown to ask the user to insert a SIM card. -->
- <string name="keyguard_missing_sim_instructions">Insert a SIM card.</string>
- <!-- Shown to ask the user to insert a SIM card when sim is missing or not readable. -->
- <string name="keyguard_missing_sim_instructions_long">The SIM card is missing or not readable. Insert a SIM card.</string>
- <!-- Shown when SIM card is permanently disabled. -->
- <string name="keyguard_permanent_disabled_sim_message_short">Unusable SIM card.</string>
- <!-- Shown to inform the user to SIM card is permanently disabled. -->
- <string name="keyguard_permanent_disabled_sim_instructions">Your SIM card has been permanently disabled.\n
- Contact your wireless service provider for another SIM card.</string>
+ <!-- Shown when there is no SIM. -->
+ <string name="keyguard_missing_sim_message_short">No SIM</string>
+ <!-- Shown to ask the user to add a SIM. -->
+ <string name="keyguard_missing_sim_instructions">Add a SIM.</string>
+ <!-- Shown to ask the user to add a SIM when sim is missing or not readable. -->
+ <string name="keyguard_missing_sim_instructions_long">The SIM is missing or not readable. Add a SIM.</string>
+ <!-- Shown when SIM is permanently disabled. -->
+ <string name="keyguard_permanent_disabled_sim_message_short">Unusable SIM.</string>
+ <!-- Shown to inform the user to SIM is permanently deactivated. -->
+ <string name="keyguard_permanent_disabled_sim_instructions">Your SIM has been permanently deactivated.\n
+ Contact your wireless service provider for another SIM.</string>
<!-- Shown to tell the user that their SIM is locked and they must unlock it. -->
- <string name="keyguard_sim_locked_message">SIM card is locked.</string>
+ <string name="keyguard_sim_locked_message">SIM is locked.</string>
<!-- When the user enters a wrong sim pin too many times, it becomes PUK locked (Pin Unlock Kode) -->
- <string name="keyguard_sim_puk_locked_message">SIM card is PUK-locked.</string>
+ <string name="keyguard_sim_puk_locked_message">SIM is PUK-locked.</string>
<!-- For the unlock screen, When the user enters a sim unlock code, it takes a little while to check
whether it is valid, and to unlock the sim if it is valid. we display a
progress dialog in the meantime. this is the emssage. -->
- <string name="keyguard_sim_unlock_progress_dialog_message">Unlocking SIM card\u2026</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message">Unlocking SIM\u2026</string>
<!-- Composes together the carrier name and the SIM card locked message. Example: CarrierName (SIM LOCKED) -->
<string name="keyguard_carrier_name_with_sim_locked_template" translatable="false"><xliff:g id="carrier">%s</xliff:g> (<xliff:g id="message">%s</xliff:g>)</string>
@@ -139,8 +139,8 @@
<string name="kg_puk_enter_pin_hint">Enter desired PIN code</string>
<!-- Message shown when the user needs to confirm the PIN they just entered in the PUK screen -->
<string name="kg_enter_confirm_pin_hint">Confirm desired PIN code</string>
- <!-- Message shown in dialog while the device is unlocking the SIM card -->
- <string name="kg_sim_unlock_progress_dialog_message">Unlocking SIM card\u2026</string>
+ <!-- Message shown in dialog while the device is unlocking the SIM -->
+ <string name="kg_sim_unlock_progress_dialog_message">Unlocking SIM\u2026</string>
<!-- Message shown when the user enters an invalid SIM pin password in PUK screen -->
<string name="kg_invalid_sim_pin_hint">Type a PIN that is 4 to 8 numbers.</string>
<!-- Message shown when the user enters an invalid PUK code in the PUK screen -->
diff --git a/packages/SystemUI/res-product/values/strings.xml b/packages/SystemUI/res-product/values/strings.xml
index b71caef..75c8286 100644
--- a/packages/SystemUI/res-product/values/strings.xml
+++ b/packages/SystemUI/res-product/values/strings.xml
@@ -28,10 +28,10 @@
<!-- Message of the overlay warning the user to interact with the device or it will go to sleep. [CHAR LIMIT=NONE] -->
<string name="inattentive_sleep_warning_message" product="default">The device will soon turn off; press to keep it on.</string>
- <!-- Shown when there is no SIM card. -->
- <string name="keyguard_missing_sim_message" product="tablet">No SIM card in tablet.</string>
- <!-- Shown when there is no SIM card. -->
- <string name="keyguard_missing_sim_message" product="default">No SIM card in phone.</string>
+ <!-- Shown when there is no SIM. -->
+ <string name="keyguard_missing_sim_message" product="tablet">No SIM in tablet.</string>
+ <!-- Shown when there is no SIM. -->
+ <string name="keyguard_missing_sim_message" product="default">No SIM in phone.</string>
<!-- String shown in PUK screen when PIN codes don't match -->
<string name="kg_invalid_confirm_pin_hint" product="default">PIN codes does not match</string>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index ded466a..2727c83 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -23,8 +23,8 @@
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.res.Configuration;
+import android.content.res.Configuration.Orientation;
import android.metrics.LogMaker;
-import android.util.Log;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
@@ -75,6 +75,7 @@
@Nullable
private Consumer<Boolean> mMediaVisibilityChangedListener;
+ @Orientation
private int mLastOrientation;
private String mCachedSpecs = "";
@Nullable
@@ -88,21 +89,16 @@
new QSPanel.OnConfigurationChangedListener() {
@Override
public void onConfigurationChange(Configuration newConfig) {
+ mQSLogger.logOnConfigurationChanged(
+ /* lastOrientation= */ mLastOrientation,
+ /* newOrientation= */ newConfig.orientation,
+ /* containerName= */ mView.getDumpableTag());
+
mShouldUseSplitNotificationShade =
- LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
- // Logging to aid the investigation of b/216244185.
- Log.d(TAG,
- "onConfigurationChange: "
- + "mShouldUseSplitNotificationShade="
- + mShouldUseSplitNotificationShade + ", "
- + "newConfig.windowConfiguration="
- + newConfig.windowConfiguration);
- mQSLogger.logOnConfigurationChanged(mLastOrientation, newConfig.orientation,
- mView.getDumpableTag());
- if (newConfig.orientation != mLastOrientation) {
- mLastOrientation = newConfig.orientation;
- switchTileLayout(false);
- }
+ LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
+ mLastOrientation = newConfig.orientation;
+
+ switchTileLayoutIfNeeded();
onConfigurationChanged();
}
};
@@ -334,6 +330,10 @@
}
}
+ private void switchTileLayoutIfNeeded() {
+ switchTileLayout(/* force= */ false);
+ }
+
boolean switchTileLayout(boolean force) {
/* Whether or not the panel currently contains a media player. */
boolean horizontal = shouldUseHorizontalLayout();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 3cad2a0..b847ad0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -277,7 +277,7 @@
// Then the layout changes
assertThat(mController.shouldUseHorizontalLayout()).isTrue();
- verify(mHorizontalLayoutListener).run(); // not invoked
+ verify(mHorizontalLayoutListener).run();
// When it is rotated back to portrait
mConfiguration.orientation = Configuration.ORIENTATION_PORTRAIT;
@@ -300,4 +300,24 @@
verify(mQSTile).refreshState();
verify(mOtherTile, never()).refreshState();
}
+
+ @Test
+ public void configurationChange_onlySplitShadeConfigChanges_horizontalLayoutStatusUpdated() {
+ // Preconditions for horizontal layout
+ when(mMediaHost.getVisible()).thenReturn(true);
+ when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(false);
+ mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ mController.setUsingHorizontalLayoutChangeListener(mHorizontalLayoutListener);
+ mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration);
+ assertThat(mController.shouldUseHorizontalLayout()).isTrue();
+ reset(mHorizontalLayoutListener);
+
+ // Only split shade status changes
+ when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
+ mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration);
+
+ // Horizontal layout is updated accordingly.
+ assertThat(mController.shouldUseHorizontalLayout()).isFalse();
+ verify(mHorizontalLayoutListener).run();
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2a4bcb0..1e9c3b7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -657,25 +657,27 @@
userState.mBindingServices.removeIf(filter);
userState.mCrashedServices.removeIf(filter);
final Iterator<ComponentName> it = userState.mEnabledServices.iterator();
+ boolean anyServiceRemoved = false;
while (it.hasNext()) {
final ComponentName comp = it.next();
final String compPkg = comp.getPackageName();
if (compPkg.equals(packageName)) {
it.remove();
- // Update the enabled services setting.
- persistComponentNamesToSettingLocked(
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- userState.mEnabledServices, userId);
- // Update the touch exploration granted services setting.
userState.mTouchExplorationGrantedServices.remove(comp);
- persistComponentNamesToSettingLocked(
- Settings.Secure.
- TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
- userState.mTouchExplorationGrantedServices, userId);
- onUserStateChangedLocked(userState);
- return;
+ anyServiceRemoved = true;
}
}
+ if (anyServiceRemoved) {
+ // Update the enabled services setting.
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ userState.mEnabledServices, userId);
+ // Update the touch exploration granted services setting.
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
+ userState.mTouchExplorationGrantedServices, userId);
+ onUserStateChangedLocked(userState);
+ }
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 38237fa..34b7a8a 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -573,12 +573,6 @@
}
}
- if (onClickIntent != null) {
- views.setOnClickPendingIntent(android.R.id.background,
- PendingIntent.getActivity(mContext, 0, onClickIntent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
- }
-
Icon icon = appInfo.icon != 0
? Icon.createWithResource(appInfo.packageName, appInfo.icon)
: Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
@@ -590,6 +584,12 @@
for (int j = 0; j < widgetCount; j++) {
Widget widget = provider.widgets.get(j);
if (targetWidget != null && targetWidget != widget) continue;
+ if (onClickIntent != null) {
+ views.setOnClickPendingIntent(android.R.id.background,
+ PendingIntent.getActivity(mContext, widget.appWidgetId, onClickIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT
+ | PendingIntent.FLAG_IMMUTABLE));
+ }
if (widget.replaceWithMaskedViewsLocked(views)) {
scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 75e30e5..6bd036b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13529,7 +13529,7 @@
}
} else {
BroadcastFilter bf = (BroadcastFilter)target;
- if (bf.requiredPermission == null) {
+ if (bf.exported && bf.requiredPermission == null) {
allProtected = false;
break;
}
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index da209f0..5cdcd42 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -41,3 +41,5 @@
# Multiuser
per-file User* = file:/MULTIUSER_OWNERS
+# Broadcasts
+per-file Broadcast* = file:/BROADCASTS_OWNERS
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index bda60ff..8624ee0 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -379,11 +379,16 @@
resolvedType = key.requestResolvedType;
}
- // Apply any launch flags from the ActivityOptions. This is to ensure that the caller
- // can specify a consistent launch mode even if the PendingIntent is immutable
+ // Apply any launch flags from the ActivityOptions. This is used only by SystemUI
+ // to ensure that we can launch the pending intent with a consistent launch mode even
+ // if the provided PendingIntent is immutable (ie. to force an activity to launch into
+ // a new task, or to launch multiple instances if supported by the app)
final ActivityOptions opts = ActivityOptions.fromBundle(options);
if (opts != null) {
- finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
+ // TODO(b/254490217): Move this check into SafeActivityOptions
+ if (controller.mAtmInternal.isCallerRecents(Binder.getCallingUid())) {
+ finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
+ }
}
// Extract options before clearing calling identity
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 01afd18..e0d324a 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -124,6 +124,8 @@
import android.media.audiopolicy.AudioProductStrategy;
import android.media.audiopolicy.AudioVolumeGroup;
import android.media.audiopolicy.IAudioPolicyCallback;
+import android.media.permission.ClearCallingIdentityContext;
+import android.media.permission.SafeCloseable;
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionCallback;
import android.media.projection.IMediaProjectionManager;
@@ -11047,6 +11049,33 @@
}
}
+ /** @see AudioManager#supportsBluetoothVariableLatency() */
+ @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public boolean supportsBluetoothVariableLatency() {
+ super.supportsBluetoothVariableLatency_enforcePermission();
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ return AudioSystem.supportsBluetoothVariableLatency();
+ }
+ }
+
+ /** @see AudioManager#setBluetoothVariableLatencyEnabled(boolean) */
+ @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public void setBluetoothVariableLatencyEnabled(boolean enabled) {
+ super.setBluetoothVariableLatencyEnabled_enforcePermission();
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ AudioSystem.setBluetoothVariableLatencyEnabled(enabled);
+ }
+ }
+
+ /** @see AudioManager#isBluetoothVariableLatencyEnabled(boolean) */
+ @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public boolean isBluetoothVariableLatencyEnabled() {
+ super.isBluetoothVariableLatencyEnabled_enforcePermission();
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ return AudioSystem.isBluetoothVariableLatencyEnabled();
+ }
+ }
+
private final Object mExtVolumeControllerLock = new Object();
private IAudioPolicyCallback mExtVolumeController;
private void setExtVolumeController(IAudioPolicyCallback apc) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 4a0a07b..dc8fcb0 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -94,7 +94,7 @@
void networkBlocked(int uid, @Nullable UidBlockedState uidBlockedState) {
synchronized (mLock) {
if (LOGD || uid == mDebugUid) {
- Slog.d(TAG, "Blocked state of " + uid + ": " + uidBlockedState.toString());
+ Slog.d(TAG, "Blocked state of " + uid + ": " + uidBlockedState);
}
if (uidBlockedState == null) {
mNetworkBlockedBuffer.networkBlocked(uid, BLOCKED_REASON_NONE, ALLOWED_REASON_NONE,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7468d32..b6bdd4c 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1977,34 +1977,39 @@
return (haystack & needle) != 0;
}
- public boolean isInLockDownMode() {
- return mIsInLockDownMode;
+ // Return whether the user is in lockdown mode.
+ // If the flag is not set, we assume the user is not in lockdown.
+ public boolean isInLockDownMode(int userId) {
+ return mUserInLockDownMode.get(userId, false);
}
@Override
public synchronized void onStrongAuthRequiredChanged(int userId) {
boolean userInLockDownModeNext = containsFlag(getStrongAuthForUser(userId),
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
- mUserInLockDownMode.put(userId, userInLockDownModeNext);
- boolean isInLockDownModeNext = mUserInLockDownMode.indexOfValue(true) != -1;
- if (mIsInLockDownMode == isInLockDownModeNext) {
+ // Nothing happens if the lockdown mode of userId keeps the same.
+ if (userInLockDownModeNext == isInLockDownMode(userId)) {
return;
}
- if (isInLockDownModeNext) {
- cancelNotificationsWhenEnterLockDownMode();
+ // When the lockdown mode is changed, we perform the following steps.
+ // If the userInLockDownModeNext is true, all the function calls to
+ // notifyPostedLocked and notifyRemovedLocked will not be executed.
+ // The cancelNotificationsWhenEnterLockDownMode calls notifyRemovedLocked
+ // and postNotificationsWhenExitLockDownMode calls notifyPostedLocked.
+ // So we shall call cancelNotificationsWhenEnterLockDownMode before
+ // we set mUserInLockDownMode as true.
+ // On the other hand, if the userInLockDownModeNext is false, we shall call
+ // postNotificationsWhenExitLockDownMode after we put false into mUserInLockDownMode
+ if (userInLockDownModeNext) {
+ cancelNotificationsWhenEnterLockDownMode(userId);
}
- // When the mIsInLockDownMode is true, both notifyPostedLocked and
- // notifyRemovedLocked will be dismissed. So we shall call
- // cancelNotificationsWhenEnterLockDownMode before we set mIsInLockDownMode
- // as true and call postNotificationsWhenExitLockDownMode after we set
- // mIsInLockDownMode as false.
- mIsInLockDownMode = isInLockDownModeNext;
+ mUserInLockDownMode.put(userId, userInLockDownModeNext);
- if (!isInLockDownModeNext) {
- postNotificationsWhenExitLockDownMode();
+ if (!userInLockDownModeNext) {
+ postNotificationsWhenExitLockDownMode(userId);
}
}
}
@@ -4953,16 +4958,7 @@
}
enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
- // If the caller is system, take the package name from the rule's owner rather than
- // from the caller's package.
- String rulePkg = pkg;
- if (isCallingUidSystem()) {
- if (automaticZenRule.getOwner() != null) {
- rulePkg = automaticZenRule.getOwner().getPackageName();
- }
- }
-
- return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule,
+ return mZenModeHelper.addAutomaticZenRule(pkg, automaticZenRule,
"addAutomaticZenRule");
}
@@ -9713,11 +9709,14 @@
}
}
- private void cancelNotificationsWhenEnterLockDownMode() {
+ private void cancelNotificationsWhenEnterLockDownMode(int userId) {
synchronized (mNotificationLock) {
int numNotifications = mNotificationList.size();
for (int i = 0; i < numNotifications; i++) {
NotificationRecord rec = mNotificationList.get(i);
+ if (rec.getUser().getIdentifier() != userId) {
+ continue;
+ }
mListeners.notifyRemovedLocked(rec, REASON_CANCEL_ALL,
rec.getStats());
}
@@ -9725,14 +9724,23 @@
}
}
- private void postNotificationsWhenExitLockDownMode() {
+ private void postNotificationsWhenExitLockDownMode(int userId) {
synchronized (mNotificationLock) {
int numNotifications = mNotificationList.size();
+ // Set the delay to spread out the burst of notifications.
+ long delay = 0;
for (int i = 0; i < numNotifications; i++) {
NotificationRecord rec = mNotificationList.get(i);
- mListeners.notifyPostedLocked(rec, rec);
+ if (rec.getUser().getIdentifier() != userId) {
+ continue;
+ }
+ mHandler.postDelayed(() -> {
+ synchronized (mNotificationLock) {
+ mListeners.notifyPostedLocked(rec, rec);
+ }
+ }, delay);
+ delay += 20;
}
-
}
}
@@ -9911,6 +9919,9 @@
for (int i = 0; i < N; i++) {
NotificationRecord record = mNotificationList.get(i);
+ if (isInLockDownMode(record.getUser().getIdentifier())) {
+ continue;
+ }
if (!isVisibleToListener(record.getSbn(), record.getNotificationType(), info)) {
continue;
}
@@ -9952,8 +9963,8 @@
rankings.toArray(new NotificationListenerService.Ranking[0]));
}
- boolean isInLockDownMode() {
- return mStrongAuthTracker.isInLockDownMode();
+ boolean isInLockDownMode(int userId) {
+ return mStrongAuthTracker.isInLockDownMode(userId);
}
boolean hasCompanionDevice(ManagedServiceInfo info) {
@@ -11015,7 +11026,7 @@
@GuardedBy("mNotificationLock")
void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
boolean notifyAllListeners) {
- if (isInLockDownMode()) {
+ if (isInLockDownMode(r.getUser().getIdentifier())) {
return;
}
@@ -11121,7 +11132,7 @@
@GuardedBy("mNotificationLock")
public void notifyRemovedLocked(NotificationRecord r, int reason,
NotificationStats notificationStats) {
- if (isInLockDownMode()) {
+ if (isInLockDownMode(r.getUser().getIdentifier())) {
return;
}
@@ -11170,10 +11181,6 @@
*/
@GuardedBy("mNotificationLock")
public void notifyRankingUpdateLocked(List<NotificationRecord> changedHiddenNotifications) {
- if (isInLockDownMode()) {
- return;
- }
-
boolean isHiddenRankingUpdate = changedHiddenNotifications != null
&& changedHiddenNotifications.size() > 0;
// TODO (b/73052211): if the ranking update changed the notification type,
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 4c23ab8..d426679 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -326,7 +326,7 @@
public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule,
String reason) {
- if (!ZenModeConfig.SYSTEM_AUTHORITY.equals(pkg)) {
+ if (!isSystemRule(automaticZenRule)) {
PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner());
if (component == null) {
component = getActivityInfo(automaticZenRule.getConfigurationActivity());
@@ -582,6 +582,11 @@
}
}
+ private boolean isSystemRule(AutomaticZenRule rule) {
+ return rule.getOwner() != null
+ && ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName());
+ }
+
private ServiceInfo getServiceInfo(ComponentName owner) {
Intent queryIntent = new Intent();
queryIntent.setComponent(owner);
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index e411880..e7b1517 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -572,11 +572,14 @@
size += getDirectorySize(path);
if (!ArrayUtils.isEmpty(info.applicationInfo.splitSourceDirs)) {
for (String splitSourceDir : info.applicationInfo.splitSourceDirs) {
- path = Paths.get(splitSourceDir).toFile();
- if (path.isFile()) {
- path = path.getParentFile();
+ File pathSplitSourceDir = Paths.get(splitSourceDir).toFile();
+ if (pathSplitSourceDir.isFile()) {
+ pathSplitSourceDir = pathSplitSourceDir.getParentFile();
}
- size += getDirectorySize(path);
+ if (path.getAbsolutePath().equals(pathSplitSourceDir.getAbsolutePath())) {
+ continue;
+ }
+ size += getDirectorySize(pathSplitSourceDir);
}
}
return size;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 014d580..554e269 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -644,8 +644,8 @@
Permission bp = mRegistry.getPermission(info.name);
added = bp == null;
int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
+ enforcePermissionCapLocked(info, tree);
if (added) {
- enforcePermissionCapLocked(info, tree);
bp = new Permission(info.name, tree.getPackageName(), Permission.TYPE_DYNAMIC);
} else if (!bp.isDynamic()) {
throw new SecurityException("Not allowed to modify non-dynamic permission "
@@ -2136,6 +2136,46 @@
}
/**
+ * If the package was below api 23, got the SYSTEM_ALERT_WINDOW permission automatically, and
+ * then updated past api 23, and the app does not satisfy any of the other SAW permission flags,
+ * the permission should be revoked.
+ *
+ * @param newPackage The new package that was installed
+ * @param oldPackage The old package that was updated
+ */
+ private void revokeSystemAlertWindowIfUpgradedPast23(
+ @NonNull AndroidPackage newPackage,
+ @NonNull AndroidPackage oldPackage) {
+ if (oldPackage.getTargetSdkVersion() >= Build.VERSION_CODES.M
+ || newPackage.getTargetSdkVersion() < Build.VERSION_CODES.M
+ || !newPackage.getRequestedPermissions()
+ .contains(Manifest.permission.SYSTEM_ALERT_WINDOW)) {
+ return;
+ }
+
+ Permission saw;
+ synchronized (mLock) {
+ saw = mRegistry.getPermission(Manifest.permission.SYSTEM_ALERT_WINDOW);
+ }
+ final PackageStateInternal ps =
+ mPackageManagerInt.getPackageStateInternal(newPackage.getPackageName());
+ if (shouldGrantPermissionByProtectionFlags(newPackage, ps, saw, new ArraySet<>())
+ || shouldGrantPermissionBySignature(newPackage, saw)) {
+ return;
+ }
+ for (int userId : getAllUserIds()) {
+ try {
+ revokePermissionFromPackageForUser(newPackage.getPackageName(),
+ Manifest.permission.SYSTEM_ALERT_WINDOW, false, userId,
+ mDefaultPermissionCallback);
+ } catch (IllegalStateException | SecurityException e) {
+ Log.e(TAG, "unable to revoke SYSTEM_ALERT_WINDOW for "
+ + newPackage.getPackageName() + " user " + userId, e);
+ }
+ }
+ }
+
+ /**
* We might auto-grant permissions if any permission of the group is already granted. Hence if
* the group of a granted permission changes we need to revoke it to avoid having permissions of
* the new group auto-granted.
@@ -4661,6 +4701,7 @@
if (hasOldPkg) {
revokeRuntimePermissionsIfGroupChangedInternal(pkg, oldPkg);
revokeStoragePermissionsIfScopeExpandedInternal(pkg, oldPkg);
+ revokeSystemAlertWindowIfUpgradedPast23(pkg, oldPkg);
}
if (hasPermissionDefinitionChanges) {
revokeRuntimePermissionsIfPermissionDefinitionChangedInternal(
diff --git a/services/core/java/com/android/server/security/OWNERS b/services/core/java/com/android/server/security/OWNERS
index 5c2d5ba..5bcc98b6 100644
--- a/services/core/java/com/android/server/security/OWNERS
+++ b/services/core/java/com/android/server/security/OWNERS
@@ -1,4 +1,4 @@
# Bug component: 36824
per-file *AttestationVerification* = file:/core/java/android/security/attestationverification/OWNERS
-per-file FileIntegrityService.java = victorhsieh@google.com
+per-file FileIntegrity*.java = victorhsieh@google.com
diff --git a/services/core/java/com/android/server/security/rkp/TEST_MAPPING b/services/core/java/com/android/server/security/rkp/TEST_MAPPING
new file mode 100644
index 0000000..e983968
--- /dev/null
+++ b/services/core/java/com/android/server/security/rkp/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "presubmit": [
+ {
+ "name": "RemoteProvisioningServiceTests"
+ }
+ ]
+}
+
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 9727558..6043caa 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -1733,7 +1733,7 @@
setExternalControl(true, vibHolder.stats);
}
if (DEBUG) {
- Slog.e(TAG, "Playing external vibration: " + vib);
+ Slog.d(TAG, "Playing external vibration: " + vib);
}
// Vibrator will start receiving data from external channels after this point.
// Report current time as the vibration start time, for debugging.
@@ -1747,7 +1747,7 @@
if (mCurrentExternalVibration != null
&& mCurrentExternalVibration.isHoldingSameVibration(vib)) {
if (DEBUG) {
- Slog.e(TAG, "Stopping external vibration" + vib);
+ Slog.d(TAG, "Stopping external vibration: " + vib);
}
endExternalVibrateLocked(
new Vibration.EndInfo(Vibration.Status.FINISHED),
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index fd6c974..b160af6a 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -98,7 +98,7 @@
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
return mService.getRecentTasks().createRecentTaskInfo(task,
- false /* stripExtras */);
+ false /* stripExtras */, true /* getTasksAllowed */);
} finally {
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 4860762..1fc061b 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -976,7 +976,7 @@
continue;
}
- res.add(createRecentTaskInfo(task, true /* stripExtras */));
+ res.add(createRecentTaskInfo(task, true /* stripExtras */, getTasksAllowed));
}
return res;
}
@@ -1895,7 +1895,8 @@
/**
* Creates a new RecentTaskInfo from a Task.
*/
- ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras) {
+ ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras,
+ boolean getTasksAllowed) {
final ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
// If the recent Task is detached, we consider it will be re-attached to the default
// TaskDisplayArea because we currently only support recent overview in the default TDA.
@@ -1907,6 +1908,9 @@
rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID;
rti.persistentId = rti.taskId;
rti.lastSnapshotData.set(tr.mLastTaskSnapshotData);
+ if (!getTasksAllowed) {
+ Task.trimIneffectiveInfo(tr, rti);
+ }
// Fill in organized child task info for the task created by organizer.
if (tr.mCreatedByOrganizer) {
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 33f019e..4e339f1 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -177,6 +177,10 @@
}
// Fill in some deprecated values
rti.id = rti.taskId;
+
+ if (!mAllowed) {
+ Task.trimIneffectiveInfo(task, rti);
+ }
return rti;
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d6f295e..8993840 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3447,6 +3447,27 @@
info.isSleeping = shouldSleepActivities();
}
+ /**
+ * Removes the activity info if the activity belongs to a different uid, which is
+ * different from the app that hosts the task.
+ */
+ static void trimIneffectiveInfo(Task task, TaskInfo info) {
+ final ActivityRecord baseActivity = task.getActivity(r -> !r.finishing,
+ false /* traverseTopToBottom */);
+ final int baseActivityUid =
+ baseActivity != null ? baseActivity.getUid() : task.effectiveUid;
+
+ if (info.topActivityInfo != null
+ && task.effectiveUid != info.topActivityInfo.applicationInfo.uid) {
+ info.topActivity = null;
+ info.topActivityInfo = null;
+ }
+
+ if (task.effectiveUid != baseActivityUid) {
+ info.baseActivity = null;
+ }
+ }
+
@Nullable PictureInPictureParams getPictureInPictureParams() {
final Task topTask = getTopMostTask();
if (topTask == null) return null;
diff --git a/services/tests/RemoteProvisioningServiceTests/Android.bp b/services/tests/RemoteProvisioningServiceTests/Android.bp
new file mode 100644
index 0000000..075680a
--- /dev/null
+++ b/services/tests/RemoteProvisioningServiceTests/Android.bp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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 {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "RemoteProvisioningServiceTests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "android.security.rkp_aidl-java",
+ "androidx.test.core",
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "mockito-target",
+ "service-rkp.impl",
+ "services.core",
+ "truth-prebuilt",
+ ],
+ test_suites: [
+ "device-tests",
+ ],
+ platform_apis: true,
+}
diff --git a/services/tests/RemoteProvisioningServiceTests/AndroidManifest.xml b/services/tests/RemoteProvisioningServiceTests/AndroidManifest.xml
new file mode 100644
index 0000000..7c12e189
--- /dev/null
+++ b/services/tests/RemoteProvisioningServiceTests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.server.security.rkp.test">
+
+ <application android:testOnly="true">
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.security.rkp.test"
+ android:label="Remote Provisioning System Service Tests"/>
+</manifest>
diff --git a/services/tests/RemoteProvisioningServiceTests/AndroidTest.xml b/services/tests/RemoteProvisioningServiceTests/AndroidTest.xml
new file mode 100644
index 0000000..bf86fc8
--- /dev/null
+++ b/services/tests/RemoteProvisioningServiceTests/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<configuration description="Runs Frameworks RemoteProvisioningService Tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+ <option name="test-tag" value="RemoteProvisioningServiceTests" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="RemoteProvisioningServiceTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.server.security.rkp.test" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
+ </test>
+</configuration>
diff --git a/services/tests/RemoteProvisioningServiceTests/OWNERS b/services/tests/RemoteProvisioningServiceTests/OWNERS
new file mode 100644
index 0000000..348f940
--- /dev/null
+++ b/services/tests/RemoteProvisioningServiceTests/OWNERS
@@ -0,0 +1 @@
+file:platform/frameworks/base:master:/core/java/android/security/rkp/OWNERS
diff --git a/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java
new file mode 100644
index 0000000..470f2be
--- /dev/null
+++ b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security.rkp;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.AdditionalAnswers.answerVoid;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
+import android.os.OutcomeReceiver;
+import android.security.rkp.IGetKeyCallback;
+import android.security.rkp.service.RegistrationProxy;
+import android.security.rkp.service.RemotelyProvisionedKey;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.stubbing.Answer;
+import org.mockito.stubbing.VoidAnswer4;
+
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.concurrent.Executor;
+
+/**
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:RemoteProvisioningRegistrationTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class RemoteProvisioningRegistrationTest {
+ private RegistrationProxy mRegistrationProxy;
+ private RemoteProvisioningRegistration mRegistration;
+
+ @Before
+ public void setUp() {
+ mRegistrationProxy = mock(RegistrationProxy.class);
+ mRegistration = new RemoteProvisioningRegistration(mRegistrationProxy, Runnable::run);
+ }
+
+ // answerVoid wrapper with explicit types, avoiding long signatures when mocking getKeyAsync.
+ static Answer<Void> answerGetKeyAsync(
+ VoidAnswer4<Integer, CancellationSignal, Executor,
+ OutcomeReceiver<RemotelyProvisionedKey, Exception>> answer) {
+ return answerVoid(answer);
+ }
+
+ // matcher helper, making it easier to match the different key types
+ private android.security.rkp.RemotelyProvisionedKey matches(
+ RemotelyProvisionedKey expectedKey) {
+ return argThat((android.security.rkp.RemotelyProvisionedKey key) ->
+ Arrays.equals(key.keyBlob, expectedKey.getKeyBlob())
+ && Arrays.equals(key.encodedCertChain, expectedKey.getEncodedCertChain())
+ );
+ }
+
+ @Test
+ public void getKeySuccess() throws Exception {
+ RemotelyProvisionedKey expectedKey = mock(RemotelyProvisionedKey.class);
+ doAnswer(
+ answerGetKeyAsync((keyId, cancelSignal, executor, receiver) ->
+ executor.execute(() -> receiver.onResult(expectedKey))))
+ .when(mRegistrationProxy).getKeyAsync(eq(42), any(), any(), any());
+
+ IGetKeyCallback callback = mock(IGetKeyCallback.class);
+ mRegistration.getKey(42, callback);
+ verify(callback).onSuccess(matches(expectedKey));
+ verifyNoMoreInteractions(callback);
+ }
+
+ @Test
+ public void getKeyHandlesError() throws Exception {
+ Exception expectedException = new Exception("oops!");
+ doAnswer(
+ answerGetKeyAsync((keyId, cancelSignal, executor, receiver) ->
+ executor.execute(() -> receiver.onError(expectedException))))
+ .when(mRegistrationProxy).getKeyAsync(eq(0), any(), any(), any());
+ IGetKeyCallback callback = mock(IGetKeyCallback.class);
+ mRegistration.getKey(0, callback);
+ verify(callback).onError(eq(expectedException.getMessage()));
+ verifyNoMoreInteractions(callback);
+ }
+
+ @Test
+ public void getKeyCancelDuringProxyOperation() throws Exception {
+ IGetKeyCallback callback = mock(IGetKeyCallback.class);
+ doAnswer(
+ answerGetKeyAsync((keyId, cancelSignal, executor, receiver) -> {
+ mRegistration.cancelGetKey(callback);
+ assertThat(cancelSignal.isCanceled()).isTrue();
+ executor.execute(() -> receiver.onError(new OperationCanceledException()));
+ }))
+ .when(mRegistrationProxy).getKeyAsync(eq(Integer.MAX_VALUE), any(), any(), any());
+
+ mRegistration.getKey(Integer.MAX_VALUE, callback);
+ verify(callback).onCancel();
+ verifyNoMoreInteractions(callback);
+ }
+
+ @Test
+ public void cancelGetKeyWithInvalidCallback() throws Exception {
+ IGetKeyCallback callback = mock(IGetKeyCallback.class);
+ assertThrows(IllegalArgumentException.class, () -> mRegistration.cancelGetKey(callback));
+ }
+
+ @Test
+ public void getKeyRejectsDuplicateCallback() throws Exception {
+ IGetKeyCallback callback = mock(IGetKeyCallback.class);
+ doAnswer(
+ answerGetKeyAsync((keyId, cancelSignal, executor, receiver) -> {
+ assertThrows(IllegalArgumentException.class, () ->
+ mRegistration.getKey(0, callback));
+ executor.execute(() -> receiver.onResult(mock(RemotelyProvisionedKey.class)));
+ }))
+ .when(mRegistrationProxy).getKeyAsync(anyInt(), any(), any(), any());
+
+ mRegistration.getKey(0, callback);
+ verify(callback, times(1)).onSuccess(any());
+ verifyNoMoreInteractions(callback);
+ }
+
+ @Test
+ public void getKeyCancelAfterCompleteFails() throws Exception {
+ IGetKeyCallback callback = mock(IGetKeyCallback.class);
+ doAnswer(
+ answerGetKeyAsync((keyId, cancelSignal, executor, receiver) ->
+ executor.execute(() ->
+ receiver.onResult(mock(RemotelyProvisionedKey.class))
+ )))
+ .when(mRegistrationProxy).getKeyAsync(eq(Integer.MIN_VALUE), any(), any(), any());
+
+ mRegistration.getKey(Integer.MIN_VALUE, callback);
+ verify(callback).onSuccess(any());
+ assertThrows(IllegalArgumentException.class, () -> mRegistration.cancelGetKey(callback));
+ verifyNoMoreInteractions(callback);
+ }
+
+ @Test
+ public void getKeyCatchesExceptionFromProxy() throws Exception {
+ Exception expectedException = new RuntimeException("oops! bad input!");
+ doThrow(expectedException)
+ .when(mRegistrationProxy)
+ .getKeyAsync(anyInt(), any(), any(), any());
+
+ IGetKeyCallback callback = mock(IGetKeyCallback.class);
+ mRegistration.getKey(0, callback);
+ verify(callback).onError(eq(expectedException.getMessage()));
+ assertThrows(IllegalArgumentException.class, () -> mRegistration.cancelGetKey(callback));
+ verifyNoMoreInteractions(callback);
+ }
+
+ @Test
+ public void storeUpgradedKeySuccess() throws Exception {
+ // TODO(b/262748535)
+ }
+
+ @Test
+ public void storeUpgradedKeyFails() throws Exception {
+ // TODO(b/262748535)
+ }
+
+ @Test
+ public void storeUpgradedCatchesExceptionFromProxy() throws Exception {
+ // TODO(b/262748535)
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/companion/virtual/OWNERS b/services/tests/mockingservicestests/src/com/android/server/companion/virtual/OWNERS
new file mode 100644
index 0000000..2e475a9
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/companion/virtual/OWNERS
@@ -0,0 +1 @@
+include /services/companion/java/com/android/server/companion/virtual/OWNERS
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index c5131c8..57e403c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -435,63 +435,112 @@
@Test
public void testNotifyPostedLockedInLockdownMode() {
- NotificationRecord r = mock(NotificationRecord.class);
- NotificationRecord old = mock(NotificationRecord.class);
+ NotificationRecord r0 = mock(NotificationRecord.class);
+ NotificationRecord old0 = mock(NotificationRecord.class);
+ UserHandle uh0 = mock(UserHandle.class);
- // before the lockdown mode
- when(mNm.isInLockDownMode()).thenReturn(false);
- mListeners.notifyPostedLocked(r, old, true);
- mListeners.notifyPostedLocked(r, old, false);
- verify(r, atLeast(2)).getSbn();
+ NotificationRecord r1 = mock(NotificationRecord.class);
+ NotificationRecord old1 = mock(NotificationRecord.class);
+ UserHandle uh1 = mock(UserHandle.class);
- // in the lockdown mode
- reset(r);
- reset(old);
- when(mNm.isInLockDownMode()).thenReturn(true);
- mListeners.notifyPostedLocked(r, old, true);
- mListeners.notifyPostedLocked(r, old, false);
- verify(r, never()).getSbn();
- }
+ // Neither user0 and user1 is in the lockdown mode
+ when(r0.getUser()).thenReturn(uh0);
+ when(uh0.getIdentifier()).thenReturn(0);
+ when(mNm.isInLockDownMode(0)).thenReturn(false);
- @Test
- public void testnotifyRankingUpdateLockedInLockdownMode() {
- List chn = mock(List.class);
+ when(r1.getUser()).thenReturn(uh1);
+ when(uh1.getIdentifier()).thenReturn(1);
+ when(mNm.isInLockDownMode(1)).thenReturn(false);
- // before the lockdown mode
- when(mNm.isInLockDownMode()).thenReturn(false);
- mListeners.notifyRankingUpdateLocked(chn);
- verify(chn, atLeast(1)).size();
+ mListeners.notifyPostedLocked(r0, old0, true);
+ mListeners.notifyPostedLocked(r0, old0, false);
+ verify(r0, atLeast(2)).getSbn();
- // in the lockdown mode
- reset(chn);
- when(mNm.isInLockDownMode()).thenReturn(true);
- mListeners.notifyRankingUpdateLocked(chn);
- verify(chn, never()).size();
+ mListeners.notifyPostedLocked(r1, old1, true);
+ mListeners.notifyPostedLocked(r1, old1, false);
+ verify(r1, atLeast(2)).getSbn();
+
+ // Reset
+ reset(r0);
+ reset(old0);
+ reset(r1);
+ reset(old1);
+
+ // Only user 0 is in the lockdown mode
+ when(r0.getUser()).thenReturn(uh0);
+ when(uh0.getIdentifier()).thenReturn(0);
+ when(mNm.isInLockDownMode(0)).thenReturn(true);
+
+ when(r1.getUser()).thenReturn(uh1);
+ when(uh1.getIdentifier()).thenReturn(1);
+ when(mNm.isInLockDownMode(1)).thenReturn(false);
+
+ mListeners.notifyPostedLocked(r0, old0, true);
+ mListeners.notifyPostedLocked(r0, old0, false);
+ verify(r0, never()).getSbn();
+
+ mListeners.notifyPostedLocked(r1, old1, true);
+ mListeners.notifyPostedLocked(r1, old1, false);
+ verify(r1, atLeast(2)).getSbn();
}
@Test
public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException {
- NotificationRecord r = mock(NotificationRecord.class);
- NotificationStats rs = mock(NotificationStats.class);
+ NotificationRecord r0 = mock(NotificationRecord.class);
+ NotificationStats rs0 = mock(NotificationStats.class);
+ UserHandle uh0 = mock(UserHandle.class);
+
+ NotificationRecord r1 = mock(NotificationRecord.class);
+ NotificationStats rs1 = mock(NotificationStats.class);
+ UserHandle uh1 = mock(UserHandle.class);
+
StatusBarNotification sbn = mock(StatusBarNotification.class);
FieldSetter.setField(mNm,
NotificationManagerService.class.getDeclaredField("mHandler"),
mock(NotificationManagerService.WorkerHandler.class));
- // before the lockdown mode
- when(mNm.isInLockDownMode()).thenReturn(false);
- when(r.getSbn()).thenReturn(sbn);
- mListeners.notifyRemovedLocked(r, 0, rs);
- mListeners.notifyRemovedLocked(r, 0, rs);
- verify(r, atLeast(2)).getSbn();
+ // Neither user0 and user1 is in the lockdown mode
+ when(r0.getUser()).thenReturn(uh0);
+ when(uh0.getIdentifier()).thenReturn(0);
+ when(mNm.isInLockDownMode(0)).thenReturn(false);
+ when(r0.getSbn()).thenReturn(sbn);
- // in the lockdown mode
- reset(r);
- reset(rs);
- when(mNm.isInLockDownMode()).thenReturn(true);
- when(r.getSbn()).thenReturn(sbn);
- mListeners.notifyRemovedLocked(r, 0, rs);
- mListeners.notifyRemovedLocked(r, 0, rs);
- verify(r, never()).getSbn();
+ when(r1.getUser()).thenReturn(uh1);
+ when(uh1.getIdentifier()).thenReturn(1);
+ when(mNm.isInLockDownMode(1)).thenReturn(false);
+ when(r1.getSbn()).thenReturn(sbn);
+
+ mListeners.notifyRemovedLocked(r0, 0, rs0);
+ mListeners.notifyRemovedLocked(r0, 0, rs0);
+ verify(r0, atLeast(2)).getSbn();
+
+ mListeners.notifyRemovedLocked(r1, 0, rs1);
+ mListeners.notifyRemovedLocked(r1, 0, rs1);
+ verify(r1, atLeast(2)).getSbn();
+
+ // Reset
+ reset(r0);
+ reset(rs0);
+ reset(r1);
+ reset(rs1);
+
+ // Only user 0 is in the lockdown mode
+ when(r0.getUser()).thenReturn(uh0);
+ when(uh0.getIdentifier()).thenReturn(0);
+ when(mNm.isInLockDownMode(0)).thenReturn(true);
+ when(r0.getSbn()).thenReturn(sbn);
+
+ when(r1.getUser()).thenReturn(uh1);
+ when(uh1.getIdentifier()).thenReturn(1);
+ when(mNm.isInLockDownMode(1)).thenReturn(false);
+ when(r1.getSbn()).thenReturn(sbn);
+
+ mListeners.notifyRemovedLocked(r0, 0, rs0);
+ mListeners.notifyRemovedLocked(r0, 0, rs0);
+ verify(r0, never()).getSbn();
+
+ mListeners.notifyRemovedLocked(r1, 0, rs1);
+ mListeners.notifyRemovedLocked(r1, 0, rs1);
+ verify(r1, atLeast(2)).getSbn();
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 4d1c786..dd43a6b 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -174,6 +174,7 @@
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.NotificationListenerFilter;
import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationRankingUpdate;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenPolicy;
@@ -7604,43 +7605,6 @@
}
@Test
- public void testAddAutomaticZenRule_systemCallTakesPackageFromOwner() throws Exception {
- mService.isSystemUid = true;
- ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class);
- when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
- .thenReturn(true);
- mService.setZenHelper(mockZenModeHelper);
- ComponentName owner = new ComponentName("android", "ProviderName");
- ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build();
- boolean isEnabled = true;
- AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
- zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule, "com.android.settings");
-
- // verify that zen mode helper gets passed in a package name of "android"
- verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString());
- }
-
- @Test
- public void testAddAutomaticZenRule_nonSystemCallTakesPackageFromArg() throws Exception {
- mService.isSystemUid = false;
- ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class);
- when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
- .thenReturn(true);
- mService.setZenHelper(mockZenModeHelper);
- ComponentName owner = new ComponentName("android", "ProviderName");
- ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build();
- boolean isEnabled = true;
- AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
- zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule, "another.package");
-
- // verify that zen mode helper gets passed in the package name from the arg, not the owner
- verify(mockZenModeHelper).addAutomaticZenRule(
- eq("another.package"), eq(rule), anyString());
- }
-
- @Test
public void testAreNotificationsEnabledForPackage() throws Exception {
mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
mUid);
@@ -9837,10 +9801,10 @@
mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
- assertTrue(mStrongAuthTracker.isInLockDownMode());
- mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
+ assertTrue(mStrongAuthTracker.isInLockDownMode(mContext.getUserId()));
+ mStrongAuthTracker.setGetStrongAuthForUserReturnValue(mContext.getUserId());
mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
- assertFalse(mStrongAuthTracker.isInLockDownMode());
+ assertFalse(mStrongAuthTracker.isInLockDownMode(mContext.getUserId()));
}
@Test
@@ -9856,8 +9820,8 @@
// when entering the lockdown mode, cancel the 2 notifications.
mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
- mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
- assertTrue(mStrongAuthTracker.isInLockDownMode());
+ mStrongAuthTracker.onStrongAuthRequiredChanged(0);
+ assertTrue(mStrongAuthTracker.isInLockDownMode(0));
// the notifyRemovedLocked function is called twice due to REASON_CANCEL_ALL.
ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
@@ -9866,10 +9830,46 @@
// exit lockdown mode.
mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
- mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
+ mStrongAuthTracker.onStrongAuthRequiredChanged(0);
+ assertFalse(mStrongAuthTracker.isInLockDownMode(0));
// the notifyPostedLocked function is called twice.
- verify(mListeners, times(2)).notifyPostedLocked(any(), any());
+ verify(mWorkerHandler, times(2)).postDelayed(any(Runnable.class), anyLong());
+ //verify(mListeners, times(2)).notifyPostedLocked(any(), any());
+ }
+
+ @Test
+ public void testMakeRankingUpdateLockedInLockDownMode() {
+ // post 2 notifications from a same package
+ NotificationRecord pkgA = new NotificationRecord(mContext,
+ generateSbn("a", 1000, 9, 0), mTestNotificationChannel);
+ mService.addNotification(pkgA);
+ NotificationRecord pkgB = new NotificationRecord(mContext,
+ generateSbn("a", 1000, 9, 1), mTestNotificationChannel);
+ mService.addNotification(pkgB);
+
+ mService.setIsVisibleToListenerReturnValue(true);
+ NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(null);
+ assertEquals(2, nru.getRankingMap().getOrderedKeys().length);
+
+ // when only user 0 entering the lockdown mode, its notification will be suppressed.
+ mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
+ STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ mStrongAuthTracker.onStrongAuthRequiredChanged(0);
+ assertTrue(mStrongAuthTracker.isInLockDownMode(0));
+ assertFalse(mStrongAuthTracker.isInLockDownMode(1));
+
+ nru = mService.makeRankingUpdateLocked(null);
+ assertEquals(1, nru.getRankingMap().getOrderedKeys().length);
+
+ // User 0 exits lockdown mode. Its notification will be resumed.
+ mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
+ mStrongAuthTracker.onStrongAuthRequiredChanged(0);
+ assertFalse(mStrongAuthTracker.isInLockDownMode(0));
+ assertFalse(mStrongAuthTracker.isInLockDownMode(1));
+
+ nru = mService.makeRankingUpdateLocked(null);
+ assertEquals(2, nru.getRankingMap().getOrderedKeys().length);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
index b49e5cb..8cf74fb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
@@ -19,10 +19,12 @@
import android.companion.ICompanionDeviceManager;
import android.content.ComponentName;
import android.content.Context;
+import android.service.notification.StatusBarNotification;
import androidx.annotation.Nullable;
import com.android.internal.logging.InstanceIdSequence;
+import com.android.server.notification.ManagedServices.ManagedServiceInfo;
import java.util.HashSet;
import java.util.Set;
@@ -37,6 +39,9 @@
@Nullable
NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback;
+ @Nullable
+ Boolean mIsVisibleToListenerReturnValue = null;
+
TestableNotificationManagerService(Context context, NotificationRecordLogger logger,
InstanceIdSequence notificationInstanceIdSequence) {
super(context, logger, notificationInstanceIdSequence);
@@ -119,6 +124,19 @@
mShowReviewPermissionsNotification = setting;
}
+ protected void setIsVisibleToListenerReturnValue(boolean value) {
+ mIsVisibleToListenerReturnValue = value;
+ }
+
+ @Override
+ boolean isVisibleToListener(StatusBarNotification sbn, int notificationType,
+ ManagedServiceInfo listener) {
+ if (mIsVisibleToListenerReturnValue != null) {
+ return mIsVisibleToListenerReturnValue;
+ }
+ return super.isVisibleToListener(sbn, notificationType, listener);
+ }
+
public class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker {
private int mGetStrongAuthForUserReturnValue = 0;
StrongAuthTrackerFake(Context context) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 2ccdcaa..4550b56 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -1672,36 +1672,6 @@
}
@Test
- public void testAddAutomaticZenRule_claimedSystemOwner() {
- // Make sure anything that claims to have a "system" owner but not actually part of the
- // system package still gets limited on number of rules
- for (int i = 0; i < RULE_LIMIT_PER_PACKAGE; i++) {
- ScheduleInfo si = new ScheduleInfo();
- si.startHour = i;
- AutomaticZenRule zenRule = new AutomaticZenRule("name" + i,
- new ComponentName("android", "ScheduleConditionProvider" + i),
- null, // configuration activity
- ZenModeConfig.toScheduleConditionId(si),
- new ZenPolicy.Builder().build(),
- NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test");
- assertNotNull(id);
- }
- try {
- AutomaticZenRule zenRule = new AutomaticZenRule("name",
- new ComponentName("android", "ScheduleConditionProviderFinal"),
- null, // configuration activity
- ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
- new ZenPolicy.Builder().build(),
- NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test");
- fail("allowed too many rules to be created");
- } catch (IllegalArgumentException e) {
- // yay
- }
- }
-
- @Test
public void testAddAutomaticZenRule_CA() {
AutomaticZenRule zenRule = new AutomaticZenRule("name",
null,
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index adf694c..0462e1b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -30,6 +30,7 @@
import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.os.Process.NOBODY_UID;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -1220,20 +1221,34 @@
@Test
public void testCreateRecentTaskInfo_detachedTask() {
- final Task task = createTaskBuilder(".Task").setCreateActivity(true).build();
+ final Task task = createTaskBuilder(".Task").build();
+ new ActivityBuilder(mSupervisor.mService)
+ .setTask(task)
+ .setUid(NOBODY_UID)
+ .setComponent(getUniqueComponentName())
+ .build();
final TaskDisplayArea tda = task.getDisplayArea();
assertTrue(task.isAttached());
assertTrue(task.supportsMultiWindow());
- RecentTaskInfo info = mRecentTasks.createRecentTaskInfo(task, true);
+ RecentTaskInfo info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+ true /* getTasksAllowed */);
assertTrue(info.supportsMultiWindow);
+ info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+ false /* getTasksAllowed */);
+
+ assertTrue(info.topActivity == null);
+ assertTrue(info.topActivityInfo == null);
+ assertTrue(info.baseActivity == null);
+
// The task can be put in split screen even if it is not attached now.
task.removeImmediately();
- info = mRecentTasks.createRecentTaskInfo(task, true);
+ info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+ true /* getTasksAllowed */);
assertTrue(info.supportsMultiWindow);
@@ -1242,7 +1257,8 @@
doReturn(false).when(tda).supportsNonResizableMultiWindow();
doReturn(false).when(task).isResizeable();
- info = mRecentTasks.createRecentTaskInfo(task, true);
+ info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+ true /* getTasksAllowed */);
assertFalse(info.supportsMultiWindow);
@@ -1250,7 +1266,8 @@
// the device supports it.
doReturn(true).when(tda).supportsNonResizableMultiWindow();
- info = mRecentTasks.createRecentTaskInfo(task, true);
+ info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+ true /* getTasksAllowed */);
assertTrue(info.supportsMultiWindow);
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 13550f0..f373f6e 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -478,6 +478,14 @@
public static final String SUBSCRIPTION_TYPE = SimInfo.COLUMN_SUBSCRIPTION_TYPE;
/**
+ * TelephonyProvider column name for last used TP - message Reference
+ * <P>Type: INTEGER (int)</P> with -1 as default value
+ * TP - Message Reference valid range [0 - 255]
+ * @hide
+ */
+ public static final String TP_MESSAGE_REF = SimInfo.COLUMN_TP_MESSAGE_REF;
+
+ /**
* TelephonyProvider column name data_enabled_override_rules.
* It's a list of rules for overriding data enabled settings. The syntax is
* For example, "mms=nonDefault" indicates enabling data for mms in non-default subscription.
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 3379ce5..b32f046 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1109,6 +1109,7 @@
sb.append(", ").append(mCarrierId);
sb.append(", ").append(mSkip464Xlat);
sb.append(", ").append(mAlwaysOn);
+ sb.append(", ").append(Objects.hash(mUser, mPassword));
return sb.toString();
}
@@ -1297,8 +1298,6 @@
other.mLingeringNetworkTypeBitmask)
&& Objects.equals(this.mProfileId, other.mProfileId)
&& Objects.equals(this.mPersistent, other.mPersistent)
- && Objects.equals(this.mMvnoType, other.mMvnoType)
- && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
&& Objects.equals(this.mApnSetId, other.mApnSetId)
&& Objects.equals(this.mCarrierId, other.mCarrierId)
&& Objects.equals(this.mSkip464Xlat, other.mSkip464Xlat)
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index b51e8d3d..7a5bf06 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -249,7 +249,6 @@
ENCODING_UNKNOWN, 0, 0);
}
-
/**
* Gets an SMS-SUBMIT PDU for a destination address and a message using the specified encoding.
*
@@ -272,7 +271,7 @@
boolean statusReportRequested, byte[] header, int encoding,
int languageTable, int languageShiftTable) {
return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested,
- header, encoding, languageTable, languageShiftTable, -1);
+ header, encoding, languageTable, languageShiftTable, -1, 0);
}
/**
@@ -297,6 +296,32 @@
String destinationAddress, String message,
boolean statusReportRequested, byte[] header, int encoding,
int languageTable, int languageShiftTable, int validityPeriod) {
+ return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, header,
+ encoding, languageTable, languageShiftTable, validityPeriod, 0);
+ }
+
+ /**
+ * Gets an SMS-SUBMIT PDU for a destination address and a message using the specified encoding.
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @param header a byte array containing the data for the User Data Header.
+ * @param encoding encoding defined by constants in
+ * com.android.internal.telephony.SmsConstants.ENCODING_*
+ * @param languageTable
+ * @param languageShiftTable
+ * @param validityPeriod Validity Period of the message in Minutes.
+ * @param messageRef TP Message Reference number
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
+ * @hide
+ */
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, String message,
+ boolean statusReportRequested, byte[] header, int encoding,
+ int languageTable, int languageShiftTable, int validityPeriod, int messageRef) {
// Perform null parameter checks.
if (message == null || destinationAddress == null) {
@@ -350,7 +375,7 @@
ByteArrayOutputStream bo = getSubmitPduHead(
scAddress, destinationAddress, mtiByte,
- statusReportRequested, ret);
+ statusReportRequested, ret, messageRef);
// Skip encoding pdu if error occurs when create pdu head and the error will be handled
// properly later on encodedMessage correctness check.
@@ -496,7 +521,7 @@
String destinationAddress, String message,
boolean statusReportRequested, int validityPeriod) {
return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested,
- null, ENCODING_UNKNOWN, 0, 0, validityPeriod);
+ null, ENCODING_UNKNOWN, 0, 0, validityPeriod, 0);
}
/**
@@ -507,12 +532,13 @@
* @param destinationPort the port to deliver the message to at the destination.
* @param data the data for the message.
* @param statusReportRequested indicates whether a report is reuested for this message.
+ * @param messageRef TP Message Reference number
* @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
* encoded message. Returns null on encode error.
*/
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, int destinationPort, byte[] data,
- boolean statusReportRequested) {
+ boolean statusReportRequested, int messageRef) {
SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs();
portAddrs.destPort = destinationPort;
@@ -533,7 +559,7 @@
SubmitPdu ret = new SubmitPdu();
ByteArrayOutputStream bo = getSubmitPduHead(
scAddress, destinationAddress, (byte) 0x41, /* TP-MTI=SMS-SUBMIT, TP-UDHI=true */
- statusReportRequested, ret);
+ statusReportRequested, ret, messageRef);
// Skip encoding pdu if error occurs when create pdu head and the error will be handled
// properly later on encodedMessage correctness check.
if (bo == null) return ret;
@@ -559,6 +585,24 @@
}
/**
+ * Gets an SMS-SUBMIT PDU for a data message to a destination address & port.
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param destinationPort the port to deliver the message to at the destination.
+ * @param data the data for the message.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
+ */
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, int destinationPort, byte[] data,
+ boolean statusReportRequested) {
+ return getSubmitPdu(scAddress, destinationAddress, destinationPort, data,
+ statusReportRequested, 0);
+ }
+
+ /**
* Creates the beginning of a SUBMIT PDU.
*
* This is the part of the SUBMIT PDU that is common to the two versions of
@@ -576,6 +620,28 @@
private static ByteArrayOutputStream getSubmitPduHead(
String scAddress, String destinationAddress, byte mtiByte,
boolean statusReportRequested, SubmitPdu ret) {
+ return getSubmitPduHead(scAddress, destinationAddress, mtiByte, statusReportRequested, ret,
+ 0);
+ }
+
+ /**
+ * Creates the beginning of a SUBMIT PDU.
+ *
+ * This is the part of the SUBMIT PDU that is common to the two versions of
+ * {@link #getSubmitPdu}, one of which takes a byte array and the other of which takes a
+ * <code>String</code>.
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param mtiByte
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @param ret <code>SubmitPdu</code>.
+ * @param messageRef TP Message Reference number
+ * @return a byte array of the beginning of a SUBMIT PDU. Null for invalid destinationAddress.
+ */
+ private static ByteArrayOutputStream getSubmitPduHead(
+ String scAddress, String destinationAddress, byte mtiByte,
+ boolean statusReportRequested, SubmitPdu ret, int messageRef) {
ByteArrayOutputStream bo = new ByteArrayOutputStream(
MAX_USER_DATA_BYTES + 40);
@@ -596,7 +662,7 @@
bo.write(mtiByte);
// space for TP-Message-Reference
- bo.write(0);
+ bo.write(messageRef);
byte[] daBytes;
diff --git a/tests/VectorDrawableTest/OWNERS b/tests/VectorDrawableTest/OWNERS
new file mode 100644
index 0000000..27e1668
--- /dev/null
+++ b/tests/VectorDrawableTest/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 24939
+
+include /graphics/java/android/graphics/OWNERS