diff options
75 files changed, 2328 insertions, 418 deletions
diff --git a/BROADCASTS_OWNERS b/BROADCASTS_OWNERS new file mode 100644 index 000000000000..1ea610b1bad4 --- /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 ff9c7b130e95..f1b82da7fb0a 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -30767,7 +30767,8 @@ package android.opengl { method public static void scaleM(float[], int, float, float, float); method public static void setIdentityM(float[], int); method public static void setLookAtM(float[], int, float, float, float, float, float, float, float, float, float); - method public static void setRotateEulerM(float[], int, float, float, float); + method @Deprecated public static void setRotateEulerM(float[], int, float, float, float); + method public static void setRotateEulerM2(@NonNull float[], int, float, float, float); method public static void setRotateM(float[], int, float, float, float, float); method public static void translateM(float[], int, float[], int, float, float, float); method public static void translateM(float[], int, float, float, float); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 9ff0afee28bf..6eddc3c097c6 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -6189,6 +6189,7 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 1c9713df972a..2cde539ab1f8 100644 --- a/core/java/android/content/OWNERS +++ b/core/java/android/content/OWNERS @@ -13,3 +13,4 @@ per-file ContentCaptureOptions* = file:/core/java/android/service/contentcapture 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/hardware/OWNERS b/core/java/android/hardware/OWNERS index 3b6a564fc353..d2a2f12ec59f 100644 --- a/core/java/android/hardware/OWNERS +++ b/core/java/android/hardware/OWNERS @@ -16,3 +16,6 @@ per-file *Sensor*,*Trigger* = file:platform/frameworks/native:/services/sensorse # 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/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 468e6041eb73..5abe677d0e1f 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -88,8 +88,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -1027,6 +1027,19 @@ public class CameraMetadataNative implements Parcelable { return fixedFaceRectangles; } + private boolean setLensShadingMap(LensShadingMap lensShadingMap) { + if (lensShadingMap == null) { + return false; + } + float[] lsmArray = new float[lensShadingMap.getGainFactorCount()]; + lensShadingMap.copyGainFactors(lsmArray, 0); + setBase(CaptureResult.STATISTICS_LENS_SHADING_MAP, lsmArray); + + Size s = new Size(lensShadingMap.getRowCount(), lensShadingMap.getColumnCount()); + setBase(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE, s); + return true; + } + private LensShadingMap getLensShadingMap() { float[] lsmArray = getBase(CaptureResult.STATISTICS_LENS_SHADING_MAP); Size s = get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE); @@ -1851,6 +1864,13 @@ public class CameraMetadataNative implements Parcelable { metadata.setAERegions(value); } }); + sSetCommandMap.put(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP.getNativeKey(), + new SetCommand() { + @Override + public <T> void setValue(CameraMetadataNative metadata, T value) { + metadata.setLensShadingMap((LensShadingMap) value); + } + }); } private boolean setAvailableFormats(int[] value) { diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java index 60d8cacd19be..7c2e518b8544 100644 --- a/core/java/android/hardware/usb/UsbDeviceConnection.java +++ b/core/java/android/hardware/usb/UsbDeviceConnection.java @@ -108,6 +108,34 @@ public class UsbDeviceConnection { } /** + * 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 6ac5e8de8fa7..beb0f8d336d5 100644 --- a/core/java/android/hardware/usb/UsbRequest.java +++ b/core/java/android/hardware/usb/UsbRequest.java @@ -113,11 +113,13 @@ public class UsbRequest { * 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 @@ public class UsbRequest { */ @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 @@ public class UsbRequest { * @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 @@ public class UsbRequest { 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 @@ public class UsbRequest { * @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 @@ public class UsbRequest { * @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 935374d15d9b..911aaf317e3e 100644 --- a/core/java/android/nfc/NfcActivityManager.java +++ b/core/java/android/nfc/NfcActivityManager.java @@ -122,7 +122,7 @@ public final class NfcActivityManager extends IAppCallback.Stub 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 90645dd939ae..9f01559bd344 100644 --- a/core/java/android/nfc/TechListParcel.java +++ b/core/java/android/nfc/TechListParcel.java @@ -53,7 +53,7 @@ public class TechListParcel implements Parcelable { 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 7db61303b953..b377c927d948 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3344,9 +3344,26 @@ public final class Settings { } } - // 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 5ff926336751..ac46be76bac4 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -4803,6 +4803,13 @@ public final class Telephony { "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/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java index 5f4a9cd5141e..473134ea46f3 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 @@ public class DisplayResolveInfo implements TargetInfo, Parcelable { @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 @@ public class DisplayResolveInfo implements TargetInfo, Parcelable { } }; - 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 264e4f76d35d..4b9b7cb98dac 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 @@ public final class SelectableTargetInfo implements ChooserTargetInfo { } 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 f56ab17cb059..7bb7ddc65c6d 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 @@ public interface TargetInfo { * @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 77317d1c3c1a..47057a43bbe3 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -2952,6 +2952,30 @@ static jint android_media_AudioSystem_getDirectProfilesForAttributes(JNIEnv *env 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 @@ static const JNINativeMethod gMethods[] = (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/res/values/strings.xml b/core/res/res/values/strings.xml index 855ecfbd0ae3..73c201c5bf9d 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 1a3e927a106f..2e475a9a2742 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/coretests/src/android/hardware/camera2/OWNERS b/core/tests/coretests/src/android/hardware/camera2/OWNERS new file mode 100644 index 000000000000..f48a95c5b3a3 --- /dev/null +++ b/core/tests/coretests/src/android/hardware/camera2/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/av:/camera/OWNERS diff --git a/core/tests/coretests/src/android/hardware/camera2/impl/CaptureMetadataNativeTest.java b/core/tests/coretests/src/android/hardware/camera2/impl/CaptureMetadataNativeTest.java new file mode 100644 index 000000000000..a38c040c7746 --- /dev/null +++ b/core/tests/coretests/src/android/hardware/camera2/impl/CaptureMetadataNativeTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 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.hardware.camera2.impl; + +import static com.google.common.truth.Truth.assertThat; + +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.params.LensShadingMap; +import android.util.Size; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Arrays; + +@SmallTest +@RunWith(JUnit4.class) +/** Tests for {@link CameraMetadataNative} class. */ +public class CaptureMetadataNativeTest { + + @Test + public void setLensShadingMap() { + final Size s = new Size(10, 10); + // 4 x rows x columns + final float[] elements = new float[400]; + Arrays.fill(elements, 42); + + final LensShadingMap lensShadingMap = + new LensShadingMap(elements, s.getHeight(), s.getWidth()); + CameraMetadataNative captureResults = new CameraMetadataNative(); + captureResults.set(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP, lensShadingMap); + + final LensShadingMap output = + captureResults.get(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP); + + assertThat(output).isEqualTo(lensShadingMap); + } +} diff --git a/core/tests/nfctests/Android.bp b/core/tests/nfctests/Android.bp index 335cea140df6..c74600bbab22 100644 --- a/core/tests/nfctests/Android.bp +++ b/core/tests/nfctests/Android.bp @@ -27,6 +27,7 @@ android_test { "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 000000000000..a12bbbc6884b --- /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 8ca16071a1e4..ff42fb52d8e7 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -342,10 +342,6 @@ applications that come with the platform <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/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index 9947d34495ab..c55a781ce2a4 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -38,6 +38,7 @@ import java.security.PublicKey; 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 @@ public class AndroidKeyStoreProvider extends Provider { } 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/libs/incident/Android.bp b/libs/incident/Android.bp index ff1714da2008..a996700eed2a 100644 --- a/libs/incident/Android.bp +++ b/libs/incident/Android.bp @@ -60,6 +60,7 @@ cc_defaults { ":libincident_aidl", "src/IncidentReportArgs.cpp", ], + min_sdk_version: "29", } cc_library_shared { diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 399650d69e27..85911a37684d 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -8478,6 +8478,55 @@ public class AudioManager { } } + /** + * 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 097bb4165631..474ccc939a4c 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -2428,4 +2428,30 @@ public class AudioSystem * 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 cfcd79aab4d2..9375a0d7f42f 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -517,4 +517,16 @@ interface IAudioService { 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/ImageUtils.java b/media/java/android/media/ImageUtils.java index 2f1a36cba9d0..4957300dd062 100644 --- a/media/java/android/media/ImageUtils.java +++ b/media/java/android/media/ImageUtils.java @@ -255,10 +255,10 @@ class ImageUtils { case ImageFormat.RAW_SENSOR: case ImageFormat.RAW_PRIVATE: // round estimate, real size is unknown case ImageFormat.DEPTH16: - case ImageFormat.YCBCR_P010: estimatedBytePerPixel = 2.0; break; case PixelFormat.RGB_888: + case ImageFormat.YCBCR_P010: estimatedBytePerPixel = 3.0; break; case PixelFormat.RGBA_8888: diff --git a/opengl/java/android/opengl/Matrix.java b/opengl/java/android/opengl/Matrix.java index ce3f57ebfea1..3a636fae9a35 100644 --- a/opengl/java/android/opengl/Matrix.java +++ b/opengl/java/android/opengl/Matrix.java @@ -16,6 +16,8 @@ package android.opengl; +import androidx.annotation.NonNull; + /** * Matrix math utilities. These methods operate on OpenGL ES format * matrices and vectors stored in float arrays. @@ -640,9 +642,14 @@ public class Matrix { * @param rm returns the result * @param rmOffset index into rm where the result matrix starts * @param x angle of rotation, in degrees - * @param y angle of rotation, in degrees + * @param y is broken, do not use * @param z angle of rotation, in degrees + * + * @deprecated This method is incorrect around the y axis. This method is + * deprecated and replaced (below) by setRotateEulerM2 which + * behaves correctly */ + @Deprecated public static void setRotateEulerM(float[] rm, int rmOffset, float x, float y, float z) { x *= (float) (Math.PI / 180.0f); @@ -679,6 +686,64 @@ public class Matrix { } /** + * Converts Euler angles to a rotation matrix. + * + * @param rm returns the result + * @param rmOffset index into rm where the result matrix starts + * @param x angle of rotation, in degrees + * @param y angle of rotation, in degrees + * @param z angle of rotation, in degrees + * + * @throws IllegalArgumentException if rm is null; + * or if rmOffset + 16 > rm.length; + * rmOffset < 0 + */ + public static void setRotateEulerM2(@NonNull float[] rm, int rmOffset, + float x, float y, float z) { + if (rm == null) { + throw new IllegalArgumentException("rm == null"); + } + if (rmOffset < 0) { + throw new IllegalArgumentException("rmOffset < 0"); + } + if (rm.length < rmOffset + 16) { + throw new IllegalArgumentException("rm.length < rmOffset + 16"); + } + + x *= (float) (Math.PI / 180.0f); + y *= (float) (Math.PI / 180.0f); + z *= (float) (Math.PI / 180.0f); + float cx = (float) Math.cos(x); + float sx = (float) Math.sin(x); + float cy = (float) Math.cos(y); + float sy = (float) Math.sin(y); + float cz = (float) Math.cos(z); + float sz = (float) Math.sin(z); + float cxsy = cx * sy; + float sxsy = sx * sy; + + rm[rmOffset + 0] = cy * cz; + rm[rmOffset + 1] = -cy * sz; + rm[rmOffset + 2] = sy; + rm[rmOffset + 3] = 0.0f; + + rm[rmOffset + 4] = sxsy * cz + cx * sz; + rm[rmOffset + 5] = -sxsy * sz + cx * cz; + rm[rmOffset + 6] = -sx * cy; + rm[rmOffset + 7] = 0.0f; + + rm[rmOffset + 8] = -cxsy * cz + sx * sz; + rm[rmOffset + 9] = cxsy * sz + sx * cz; + rm[rmOffset + 10] = cx * cy; + rm[rmOffset + 11] = 0.0f; + + rm[rmOffset + 12] = 0.0f; + rm[rmOffset + 13] = 0.0f; + rm[rmOffset + 14] = 0.0f; + rm[rmOffset + 15] = 1.0f; + } + + /** * Defines a viewing transformation in terms of an eye point, a center of * view, and an up vector. * diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java index 5c796af84fef..879181f0fbd6 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 @@ public class BluetoothUtils { 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 eb53ea1d44f7..c8187b89b0ed 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.Message; 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 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> 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 fd7554f11873..cd667ca05f61 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.Xml; 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 @@ final class SettingsState { 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 @@ final class SettingsState { 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 @@ final class SettingsState { addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState); - updateMemoryUsagePerPackageLocked(packageName, oldValue, value, - oldDefaultValue, newState.getDefaultValue()); + updateMemoryUsagePerPackageLocked(packageName, newSize); scheduleWriteIfNeededLocked(); @@ -552,13 +561,18 @@ final class SettingsState { } 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 @@ final class SettingsState { } 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 @@ final class SettingsState { } @GuardedBy("mLock") - private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue, - String newValue, String oldDefaultValue, String newDefaultValue) { - if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) { - return; - } + private boolean isExemptFromMemoryUsageCap(String packageName) { + return mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED + || SYSTEM_PACKAGE_NAME.equals(packageName); + } - if (SYSTEM_PACKAGE_NAME.equals(packageName)) { + @GuardedBy("mLock") + private void checkNewMemoryUsagePerPackageLocked(String packageName, int newSize) + throws IllegalStateException { + if (isExemptFromMemoryUsageCap(packageName)) { return; } + 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 = newValueSize + newDefaultValueSize + final int deltaSize = deltaKeySize + newValueSize + newDefaultValueSize - oldValueSize - oldDefaultValueSize; + return Math.max((currentSize != null) ? currentSize + deltaSize : deltaSize, 0); + } - 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 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 @@ final class SettingsState { } 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 69eb7133f46f..f6d43292eafe 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.test.AndroidTestCase; 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 @@ public class SettingsStateTest extends AndroidTestCase { 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 979e9eaa1e84..f13d961a4647 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 d90156d451c7..0a9a93bb0e0c 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 b71caef3f593..75c82867884f 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 ded466a0cb25..2727c83ad877 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.NonNull; 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 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr @Nullable private Consumer<Boolean> mMediaVisibilityChangedListener; + @Orientation private int mLastOrientation; private String mCachedSpecs = ""; @Nullable @@ -88,21 +89,16 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr 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 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr } } + 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/src/com/android/systemui/statusbar/notification/ImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java index 76025ab7aa7d..0446165be5fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java @@ -53,7 +53,9 @@ public class ImageTransformState extends TransformState { return true; } if (otherState instanceof ImageTransformState) { - return mIcon != null && mIcon.sameAs(((ImageTransformState) otherState).getIcon()); + final Icon otherIcon = ((ImageTransformState) otherState).mIcon; + return mIcon == otherIcon || (mIcon != null && otherIcon != null && mIcon.sameAs( + otherIcon)); } return false; } 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 3cad2a005882..b847ad07cd72 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 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { // 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 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { 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 2a4bcb08b54b..1e9c3b72f57c 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 38237fa8eabd..34b7a8a29c9a 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 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } - 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 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku 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 75e30e57018c..6bd036b769c2 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } 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 da209f095897..5cdcd424d059 100644 --- a/services/core/java/com/android/server/am/OWNERS +++ b/services/core/java/com/android/server/am/OWNERS @@ -41,3 +41,5 @@ per-file ContentProviderHelper.java = varunshah@google.com, omakoto@google.com, # 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 bda60ff2172b..8624ee031a93 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 @@ public final class PendingIntentRecord extends IIntentSender.Stub { 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 01afd18233d6..e0d324a448ba 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.AudioPolicyConfig; 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 @@ public class AudioService extends IAudioService.Stub } } + /** @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/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index 4d525da220c7..9b42cfca2e68 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -36,6 +36,7 @@ import android.content.pm.UserInfo; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; +import android.net.NetworkRequest; import android.os.Handler; import android.os.PowerManager; import android.os.SystemClock; @@ -103,12 +104,12 @@ class RebootEscrowManager { /** * Number of boots until we consider the escrow data to be stale for the purposes of metrics. - * <p> - * If the delta between the current boot number and the boot number stored when the mechanism + * + * <p>If the delta between the current boot number and the boot number stored when the mechanism * was armed is under this number and the escrow mechanism fails, we report it as a failure of * the mechanism. - * <p> - * If the delta over this number and escrow fails, we will not report the metric as failed + * + * <p>If the delta over this number and escrow fails, we will not report the metric as failed * since there most likely was some other issue if the device rebooted several times before * getting to the escrow restore code. */ @@ -120,8 +121,11 @@ class RebootEscrowManager { */ private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT = 3; private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS = 30; + // 3 minutes. It's enough for the default 3 retries with 30 seconds interval - private static final int DEFAULT_WAKE_LOCK_TIMEOUT_MILLIS = 180_000; + private static final int DEFAULT_LOAD_ESCROW_BASE_TIMEOUT_MILLIS = 180_000; + // 5 seconds. An extension of the overall RoR timeout to account for overhead. + private static final int DEFAULT_LOAD_ESCROW_TIMEOUT_EXTENSION_MILLIS = 5000; @IntDef(prefix = {"ERROR_"}, value = { ERROR_NONE, @@ -133,6 +137,7 @@ class RebootEscrowManager { ERROR_PROVIDER_MISMATCH, ERROR_KEYSTORE_FAILURE, ERROR_NO_NETWORK, + ERROR_TIMEOUT_EXHAUSTED, }) @Retention(RetentionPolicy.SOURCE) @interface RebootEscrowErrorCode { @@ -147,6 +152,7 @@ class RebootEscrowManager { static final int ERROR_PROVIDER_MISMATCH = 6; static final int ERROR_KEYSTORE_FAILURE = 7; static final int ERROR_NO_NETWORK = 8; + static final int ERROR_TIMEOUT_EXHAUSTED = 9; private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE; @@ -168,6 +174,15 @@ class RebootEscrowManager { /** Notified when mRebootEscrowReady changes. */ private RebootEscrowListener mRebootEscrowListener; + /** Set when unlocking reboot escrow times out. */ + private boolean mRebootEscrowTimedOut = false; + + /** + * Set when {@link #loadRebootEscrowDataWithRetry} is called to ensure the function is only + * called once. + */ + private boolean mLoadEscrowDataWithRetry = false; + /** * Hold this lock when checking or generating the reboot escrow key. */ @@ -192,6 +207,7 @@ class RebootEscrowManager { PowerManager.WakeLock mWakeLock; + private ConnectivityManager.NetworkCallback mNetworkCallback; interface Callbacks { boolean isUserSecure(int userId); @@ -246,6 +262,11 @@ class RebootEscrowManager { "server_based_ror_enabled", false); } + public boolean waitForInternet() { + return DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_OTA, "wait_for_internet_ror", false); + } + public boolean isNetworkConnected() { final ConnectivityManager connectivityManager = mContext.getSystemService(ConnectivityManager.class); @@ -263,6 +284,38 @@ class RebootEscrowManager { NetworkCapabilities.NET_CAPABILITY_VALIDATED); } + /** + * Request network with internet connectivity with timeout. + * + * @param networkCallback callback to be executed if connectivity manager exists. + * @return true if success + */ + public boolean requestNetworkWithInternet( + ConnectivityManager.NetworkCallback networkCallback) { + final ConnectivityManager connectivityManager = + mContext.getSystemService(ConnectivityManager.class); + if (connectivityManager == null) { + return false; + } + NetworkRequest request = + new NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .build(); + + connectivityManager.requestNetwork( + request, networkCallback, getLoadEscrowTimeoutMillis()); + return true; + } + + public void stopRequestingNetwork(ConnectivityManager.NetworkCallback networkCallback) { + final ConnectivityManager connectivityManager = + mContext.getSystemService(ConnectivityManager.class); + if (connectivityManager == null) { + return; + } + connectivityManager.unregisterNetworkCallback(networkCallback); + } + public Context getContext() { return mContext; } @@ -318,6 +371,16 @@ class RebootEscrowManager { DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS); } + @VisibleForTesting + public int getLoadEscrowTimeoutMillis() { + return DEFAULT_LOAD_ESCROW_BASE_TIMEOUT_MILLIS; + } + + @VisibleForTesting + public int getWakeLockTimeoutMillis() { + return getLoadEscrowTimeoutMillis() + DEFAULT_LOAD_ESCROW_TIMEOUT_EXTENSION_MILLIS; + } + public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount, int escrowDurationInSeconds, int vbmetaDigestStatus, int durationSinceBootCompleteInSeconds) { @@ -351,13 +414,37 @@ class RebootEscrowManager { mKeyStoreManager = injector.getKeyStoreManager(); } - private void onGetRebootEscrowKeyFailed(List<UserInfo> users, int attemptCount) { + /** Wrapper function to set error code serialized through handler, */ + private void setLoadEscrowDataErrorCode(@RebootEscrowErrorCode int value, Handler handler) { + if (mInjector.waitForInternet()) { + mInjector.post( + handler, + () -> { + mLoadEscrowDataErrorCode = value; + }); + } else { + mLoadEscrowDataErrorCode = value; + } + } + + /** Wrapper function to compare and set error code serialized through handler. */ + private void compareAndSetLoadEscrowDataErrorCode( + @RebootEscrowErrorCode int expectedValue, + @RebootEscrowErrorCode int newValue, + Handler handler) { + if (expectedValue == mLoadEscrowDataErrorCode) { + setLoadEscrowDataErrorCode(newValue, handler); + } + } + + private void onGetRebootEscrowKeyFailed( + List<UserInfo> users, int attemptCount, Handler retryHandler) { Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage."); for (UserInfo user : users) { mStorage.removeRebootEscrow(user.id); } - onEscrowRestoreComplete(false, attemptCount); + onEscrowRestoreComplete(false, attemptCount, retryHandler); } void loadRebootEscrowDataIfAvailable(Handler retryHandler) { @@ -380,39 +467,130 @@ class RebootEscrowManager { mWakeLock = mInjector.getWakeLock(); if (mWakeLock != null) { mWakeLock.setReferenceCounted(false); - mWakeLock.acquire(DEFAULT_WAKE_LOCK_TIMEOUT_MILLIS); + mWakeLock.acquire(mInjector.getWakeLockTimeoutMillis()); + } + + if (mInjector.waitForInternet()) { + // Timeout to stop retrying same as the wake lock timeout. + mInjector.postDelayed( + retryHandler, + () -> { + mRebootEscrowTimedOut = true; + }, + mInjector.getLoadEscrowTimeoutMillis()); + + mInjector.post( + retryHandler, + () -> loadRebootEscrowDataOnInternet(retryHandler, users, rebootEscrowUsers)); + return; } mInjector.post(retryHandler, () -> loadRebootEscrowDataWithRetry( retryHandler, 0, users, rebootEscrowUsers)); } - void scheduleLoadRebootEscrowDataOrFail(Handler retryHandler, int attemptNumber, - List<UserInfo> users, List<UserInfo> rebootEscrowUsers) { + void scheduleLoadRebootEscrowDataOrFail( + Handler retryHandler, + int attemptNumber, + List<UserInfo> users, + List<UserInfo> rebootEscrowUsers) { Objects.requireNonNull(retryHandler); final int retryLimit = mInjector.getLoadEscrowDataRetryLimit(); final int retryIntervalInSeconds = mInjector.getLoadEscrowDataRetryIntervalSeconds(); - if (attemptNumber < retryLimit) { + if (attemptNumber < retryLimit && !mRebootEscrowTimedOut) { Slog.i(TAG, "Scheduling loadRebootEscrowData retry number: " + attemptNumber); mInjector.postDelayed(retryHandler, () -> loadRebootEscrowDataWithRetry( - retryHandler, attemptNumber, users, rebootEscrowUsers), + retryHandler, attemptNumber, users, rebootEscrowUsers), retryIntervalInSeconds * 1000); return; } + if (mInjector.waitForInternet()) { + if (mRebootEscrowTimedOut) { + Slog.w(TAG, "Failed to load reboot escrow data within timeout"); + compareAndSetLoadEscrowDataErrorCode( + ERROR_NONE, ERROR_TIMEOUT_EXHAUSTED, retryHandler); + } else { + Slog.w( + TAG, + "Failed to load reboot escrow data after " + attemptNumber + " attempts"); + compareAndSetLoadEscrowDataErrorCode( + ERROR_NONE, ERROR_RETRY_COUNT_EXHAUSTED, retryHandler); + } + onGetRebootEscrowKeyFailed(users, attemptNumber, retryHandler); + return; + } + Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts"); if (mInjector.serverBasedResumeOnReboot() && !mInjector.isNetworkConnected()) { mLoadEscrowDataErrorCode = ERROR_NO_NETWORK; } else { mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED; } - onGetRebootEscrowKeyFailed(users, attemptNumber); + onGetRebootEscrowKeyFailed(users, attemptNumber, retryHandler); } - void loadRebootEscrowDataWithRetry(Handler retryHandler, int attemptNumber, - List<UserInfo> users, List<UserInfo> rebootEscrowUsers) { + void loadRebootEscrowDataOnInternet( + Handler retryHandler, List<UserInfo> users, List<UserInfo> rebootEscrowUsers) { + + // HAL-Based RoR does not require network connectivity. + if (!mInjector.serverBasedResumeOnReboot()) { + loadRebootEscrowDataWithRetry( + retryHandler, /* attemptNumber = */ 0, users, rebootEscrowUsers); + return; + } + + mNetworkCallback = + new ConnectivityManager.NetworkCallback() { + @Override + public void onAvailable(Network network) { + compareAndSetLoadEscrowDataErrorCode( + ERROR_NO_NETWORK, ERROR_NONE, retryHandler); + + if (!mLoadEscrowDataWithRetry) { + mLoadEscrowDataWithRetry = true; + // Only kickoff retry mechanism on first onAvailable call. + loadRebootEscrowDataWithRetry( + retryHandler, + /* attemptNumber = */ 0, + users, + rebootEscrowUsers); + } + } + + @Override + public void onUnavailable() { + Slog.w(TAG, "Failed to connect to network within timeout"); + compareAndSetLoadEscrowDataErrorCode( + ERROR_NONE, ERROR_NO_NETWORK, retryHandler); + onGetRebootEscrowKeyFailed(users, /* attemptCount= */ 0, retryHandler); + } + + @Override + public void onLost(Network lostNetwork) { + // TODO(b/231660348): If network is lost, wait for network to become + // available again. + Slog.w(TAG, "Network lost, still attempting to load escrow key."); + compareAndSetLoadEscrowDataErrorCode( + ERROR_NONE, ERROR_NO_NETWORK, retryHandler); + } + }; + + // Fallback to retrying without waiting for internet on failure. + boolean success = mInjector.requestNetworkWithInternet(mNetworkCallback); + if (!success) { + loadRebootEscrowDataWithRetry( + retryHandler, /* attemptNumber = */ 0, users, rebootEscrowUsers); + } + } + + void loadRebootEscrowDataWithRetry( + Handler retryHandler, + int attemptNumber, + List<UserInfo> users, + List<UserInfo> rebootEscrowUsers) { // Fetch the key from keystore to decrypt the escrow data & escrow key; this key is // generated before reboot. Note that we will clear the escrow key even if the keystore key // is null. @@ -423,7 +601,7 @@ class RebootEscrowManager { RebootEscrowKey escrowKey; try { - escrowKey = getAndClearRebootEscrowKey(kk); + escrowKey = getAndClearRebootEscrowKey(kk, retryHandler); } catch (IOException e) { Slog.i(TAG, "Failed to load escrow key, scheduling retry.", e); scheduleLoadRebootEscrowDataOrFail(retryHandler, attemptNumber + 1, users, @@ -438,12 +616,12 @@ class RebootEscrowManager { ? RebootEscrowProviderInterface.TYPE_SERVER_BASED : RebootEscrowProviderInterface.TYPE_HAL; if (providerType != mStorage.getInt(REBOOT_ESCROW_KEY_PROVIDER, -1, USER_SYSTEM)) { - mLoadEscrowDataErrorCode = ERROR_PROVIDER_MISMATCH; + setLoadEscrowDataErrorCode(ERROR_PROVIDER_MISMATCH, retryHandler); } else { - mLoadEscrowDataErrorCode = ERROR_LOAD_ESCROW_KEY; + setLoadEscrowDataErrorCode(ERROR_LOAD_ESCROW_KEY, retryHandler); } } - onGetRebootEscrowKeyFailed(users, attemptNumber + 1); + onGetRebootEscrowKeyFailed(users, attemptNumber + 1, retryHandler); return; } @@ -454,10 +632,10 @@ class RebootEscrowManager { allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey, kk); } - if (!allUsersUnlocked && mLoadEscrowDataErrorCode == ERROR_NONE) { - mLoadEscrowDataErrorCode = ERROR_UNLOCK_ALL_USERS; + if (!allUsersUnlocked) { + compareAndSetLoadEscrowDataErrorCode(ERROR_NONE, ERROR_UNLOCK_ALL_USERS, retryHandler); } - onEscrowRestoreComplete(allUsersUnlocked, attemptNumber + 1); + onEscrowRestoreComplete(allUsersUnlocked, attemptNumber + 1, retryHandler); } private void clearMetricsStorage() { @@ -497,7 +675,8 @@ class RebootEscrowManager { .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MISMATCH; } - private void reportMetricOnRestoreComplete(boolean success, int attemptCount) { + private void reportMetricOnRestoreComplete( + boolean success, int attemptCount, Handler retryHandler) { int serviceType = mInjector.serverBasedResumeOnReboot() ? FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__SERVER_BASED : FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__HAL; @@ -511,52 +690,69 @@ class RebootEscrowManager { } int vbmetaDigestStatus = getVbmetaDigestStatusOnRestoreComplete(); - if (!success && mLoadEscrowDataErrorCode == ERROR_NONE) { - mLoadEscrowDataErrorCode = ERROR_UNKNOWN; - } - - Slog.i(TAG, "Reporting RoR recovery metrics, success: " + success + ", service type: " - + serviceType + ", error code: " + mLoadEscrowDataErrorCode); + if (!success) { + compareAndSetLoadEscrowDataErrorCode(ERROR_NONE, ERROR_UNKNOWN, retryHandler); + } + + Slog.i( + TAG, + "Reporting RoR recovery metrics, success: " + + success + + ", service type: " + + serviceType + + ", error code: " + + mLoadEscrowDataErrorCode); // TODO(179105110) report the duration since boot complete. - mInjector.reportMetric(success, mLoadEscrowDataErrorCode, serviceType, attemptCount, - escrowDurationInSeconds, vbmetaDigestStatus, -1); - - mLoadEscrowDataErrorCode = ERROR_NONE; + mInjector.reportMetric( + success, + mLoadEscrowDataErrorCode, + serviceType, + attemptCount, + escrowDurationInSeconds, + vbmetaDigestStatus, + -1); + + setLoadEscrowDataErrorCode(ERROR_NONE, retryHandler); } - private void onEscrowRestoreComplete(boolean success, int attemptCount) { + private void onEscrowRestoreComplete(boolean success, int attemptCount, Handler retryHandler) { int previousBootCount = mStorage.getInt(REBOOT_ESCROW_ARMED_KEY, -1, USER_SYSTEM); int bootCountDelta = mInjector.getBootCount() - previousBootCount; if (success || (previousBootCount != -1 && bootCountDelta <= BOOT_COUNT_TOLERANCE)) { - reportMetricOnRestoreComplete(success, attemptCount); + reportMetricOnRestoreComplete(success, attemptCount, retryHandler); } - // Clear the old key in keystore. A new key will be generated by new RoR requests. mKeyStoreManager.clearKeyStoreEncryptionKey(); // Clear the saved reboot escrow provider mInjector.clearRebootEscrowProvider(); clearMetricsStorage(); + if (mNetworkCallback != null) { + mInjector.stopRequestingNetwork(mNetworkCallback); + } + if (mWakeLock != null) { mWakeLock.release(); } } - private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk) throws IOException { + private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk, Handler retryHandler) + throws IOException { RebootEscrowProviderInterface rebootEscrowProvider = mInjector.createRebootEscrowProviderIfNeeded(); if (rebootEscrowProvider == null) { - Slog.w(TAG, + Slog.w( + TAG, "Had reboot escrow data for users, but RebootEscrowProvider is unavailable"); - mLoadEscrowDataErrorCode = ERROR_NO_PROVIDER; + setLoadEscrowDataErrorCode(ERROR_NO_PROVIDER, retryHandler); return null; } // Server based RoR always need the decryption key from keystore. if (rebootEscrowProvider.getType() == RebootEscrowProviderInterface.TYPE_SERVER_BASED && kk == null) { - mLoadEscrowDataErrorCode = ERROR_KEYSTORE_FAILURE; + setLoadEscrowDataErrorCode(ERROR_KEYSTORE_FAILURE, retryHandler); return null; } @@ -870,6 +1066,9 @@ class RebootEscrowManager { pw.print("mRebootEscrowListener="); pw.println(mRebootEscrowListener); + pw.print("mLoadEscrowDataErrorCode="); + pw.println(mLoadEscrowDataErrorCode); + boolean keySet; synchronized (mKeyGenerationLock) { keySet = mPendingRebootEscrowKey != null; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 7468d32e4a01..b6bdd4c91bd9 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 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { } 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 @@ public class NotificationManagerService extends SystemService { } } - 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 @@ public class NotificationManagerService extends SystemService { } } - 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 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") void notifyPostedLocked(NotificationRecord r, NotificationRecord old, boolean notifyAllListeners) { - if (isInLockDownMode()) { + if (isInLockDownMode(r.getUser().getIdentifier())) { return; } @@ -11121,7 +11132,7 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") public void notifyRemovedLocked(NotificationRecord r, int reason, NotificationStats notificationStats) { - if (isInLockDownMode()) { + if (isInLockDownMode(r.getUser().getIdentifier())) { return; } @@ -11170,10 +11181,6 @@ public class NotificationManagerService extends SystemService { */ @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 4c23ab84a14f..d42667951608 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 class ZenModeHelper { 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 @@ public class ZenModeHelper { } } + 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 e41188049876..e7b15175f421 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 @@ public final class BackgroundDexOptService { 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/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java index 14075301d523..e0561519c23b 100644 --- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java +++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java @@ -298,7 +298,9 @@ public class ArtStatsLogUtils { dexMetadataType, apkType, ISA_MAP.getOrDefault(isa, - ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_UNKNOWN)); + ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_UNKNOWN), + ArtStatsLog.ART_DATUM_REPORTED__GC__ART_GC_COLLECTOR_TYPE_UNKNOWN, + ArtStatsLog.ART_DATUM_REPORTED__UFFD_SUPPORT__ART_UFFD_SUPPORT_UNKNOWN); } } 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 014d580e520f..554e2690b878 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 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt 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 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } /** + * 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 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt if (hasOldPkg) { revokeRuntimePermissionsIfGroupChangedInternal(pkg, oldPkg); revokeStoragePermissionsIfScopeExpandedInternal(pkg, oldPkg); + revokeSystemAlertWindowIfUpgradedPast23(pkg, oldPkg); } if (hasPermissionDefinitionChanges) { revokeRuntimePermissionsIfPermissionDefinitionChangedInternal( diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index ed4ba0d0eacd..7f8f40626c56 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -3202,8 +3202,7 @@ public final class PowerManagerService extends SystemService } final PowerGroup powerGroup = mPowerGroups.get(groupId); wakefulness = powerGroup.getWakefulnessLocked(); - if ((wakefulness == WAKEFULNESS_DREAMING || wakefulness == WAKEFULNESS_DOZING) && - powerGroup.isSandmanSummonedLocked() && powerGroup.isReadyLocked()) { + if (powerGroup.isSandmanSummonedLocked() && powerGroup.isReadyLocked()) { startDreaming = canDreamLocked(powerGroup) || canDozeLocked(powerGroup); powerGroup.setSandmanSummonedLocked(/* isSandmanSummoned= */ false); } else { 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 000000000000..e98396831a1d --- /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 97275583ac36..6043caa803e5 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 fd6c9743cb6b..b160af6a3e11 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 @@ class AppTaskImpl extends IAppTask.Stub { 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 4860762a5f7f..1fc061b2ca78 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 @@ class RecentTasks { continue; } - res.add(createRecentTaskInfo(task, true /* stripExtras */)); + res.add(createRecentTaskInfo(task, true /* stripExtras */, getTasksAllowed)); } return res; } @@ -1895,7 +1895,8 @@ class RecentTasks { /** * 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 @@ class RecentTasks { 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 33f019e0c9fb..4e339f1867ae 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 @@ class RunningTasks { } // 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 d6f295eb8f44..8993840a529e 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 @@ class Task extends TaskFragment { 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 000000000000..075680a51af3 --- /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 000000000000..7c12e1895e5c --- /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 000000000000..bf86fc8e96d6 --- /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 000000000000..348f94048311 --- /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 000000000000..470f2bec684c --- /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 000000000000..2e475a9a2742 --- /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/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java index b01c1c8ead28..ce6bd6ccbe51 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java @@ -50,6 +50,8 @@ import android.content.Context; import android.content.ContextWrapper; import android.content.pm.UserInfo; import android.hardware.rebootescrow.IRebootEscrow; +import android.net.ConnectivityManager; +import android.net.Network; import android.os.Handler; import android.os.HandlerThread; import android.os.RemoteException; @@ -72,6 +74,7 @@ import org.mockito.ArgumentCaptor; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.function.Consumer; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; @@ -113,6 +116,7 @@ public class RebootEscrowManagerTests { private RebootEscrowManager mService; private SecretKey mAesKey; private MockInjector mMockInjector; + private Handler mHandler; public interface MockableRebootEscrowInjected { int getBootCount(); @@ -132,6 +136,9 @@ public class RebootEscrowManagerTests { private final RebootEscrowKeyStoreManager mKeyStoreManager; private boolean mServerBased; private RebootEscrowProviderInterface mRebootEscrowProviderInUse; + private ConnectivityManager.NetworkCallback mNetworkCallback; + private Consumer<ConnectivityManager.NetworkCallback> mNetworkConsumer; + private boolean mWaitForInternet; MockInjector(Context context, UserManager userManager, IRebootEscrow rebootEscrow, @@ -142,6 +149,7 @@ public class RebootEscrowManagerTests { mRebootEscrow = rebootEscrow; mServiceConnection = null; mServerBased = false; + mWaitForInternet = false; RebootEscrowProviderHalImpl.Injector halInjector = new RebootEscrowProviderHalImpl.Injector() { @Override @@ -164,6 +172,7 @@ public class RebootEscrowManagerTests { mServiceConnection = serviceConnection; mRebootEscrow = null; mServerBased = true; + mWaitForInternet = false; RebootEscrowProviderServerBasedImpl.Injector injector = new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection) { @Override @@ -199,11 +208,33 @@ public class RebootEscrowManagerTests { } @Override + public boolean waitForInternet() { + return mWaitForInternet; + } + + public void setWaitForNetwork(boolean waitForNetworkEnabled) { + mWaitForInternet = waitForNetworkEnabled; + } + + @Override public boolean isNetworkConnected() { return false; } @Override + public boolean requestNetworkWithInternet( + ConnectivityManager.NetworkCallback networkCallback) { + mNetworkCallback = networkCallback; + mNetworkConsumer.accept(networkCallback); + return true; + } + + @Override + public void stopRequestingNetwork(ConnectivityManager.NetworkCallback networkCallback) { + mNetworkCallback = null; + } + + @Override public RebootEscrowProviderInterface createRebootEscrowProviderIfNeeded() { mRebootEscrowProviderInUse = mDefaultRebootEscrowProvider; return mRebootEscrowProviderInUse; @@ -242,6 +273,12 @@ public class RebootEscrowManagerTests { } @Override + public int getLoadEscrowTimeoutMillis() { + // Timeout in 3 seconds. + return 3000; + } + + @Override public String getVbmetaDigest(boolean other) { return other ? "" : "fake digest"; } @@ -291,6 +328,9 @@ public class RebootEscrowManagerTests { mMockInjector = new MockInjector(mContext, mUserManager, mRebootEscrow, mKeyStoreManager, mStorage, mInjected); mService = new RebootEscrowManager(mMockInjector, mCallbacks, mStorage); + HandlerThread thread = new HandlerThread("RebootEscrowManagerTest"); + thread.start(); + mHandler = new Handler(thread.getLooper()); } private void setServerBasedRebootEscrowProvider() throws Exception { @@ -462,7 +502,7 @@ public class RebootEscrowManagerTests { @Test public void loadRebootEscrowDataIfAvailable_NothingAvailable_Success() throws Exception { - mService.loadRebootEscrowDataIfAvailable(null); + mService.loadRebootEscrowDataIfAvailable(mHandler); } @Test @@ -499,7 +539,7 @@ public class RebootEscrowManagerTests { eq(20), eq(0) /* vbmeta status */, anyInt()); when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue()); - mService.loadRebootEscrowDataIfAvailable(null); + mService.loadRebootEscrowDataIfAvailable(mHandler); verify(mRebootEscrow).retrieveKey(); assertTrue(metricsSuccessCaptor.getValue()); verify(mKeyStoreManager).clearKeyStoreEncryptionKey(); @@ -531,9 +571,16 @@ public class RebootEscrowManagerTests { // pretend reboot happens here when(mInjected.getBootCount()).thenReturn(1); ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); - doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), - eq(0) /* error code */, eq(2) /* Server based */, eq(1) /* attempt count */, - anyInt(), eq(0) /* vbmeta status */, anyInt()); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + eq(0) /* error code */, + eq(2) /* Server based */, + eq(1) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); when(mServiceConnection.unwrap(any(), anyLong())) .thenAnswer(invocation -> invocation.getArgument(0)); @@ -569,15 +616,23 @@ public class RebootEscrowManagerTests { when(mInjected.getBootCount()).thenReturn(1); ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); - doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), - metricsErrorCodeCaptor.capture(), eq(2) /* Server based */, - eq(1) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt()); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), + eq(2) /* Server based */, + eq(1) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(RemoteException.class); mService.loadRebootEscrowDataIfAvailable(null); verify(mServiceConnection).unwrap(any(), anyLong()); assertFalse(metricsSuccessCaptor.getValue()); - assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY), + assertEquals( + Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY), metricsErrorCodeCaptor.getValue()); } @@ -606,18 +661,24 @@ public class RebootEscrowManagerTests { when(mInjected.getBootCount()).thenReturn(1); ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); - doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), - metricsErrorCodeCaptor.capture(), eq(2) /* Server based */, - eq(2) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt()); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), + eq(2) /* Server based */, + eq(2) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(IOException.class); - HandlerThread thread = new HandlerThread("RebootEscrowManagerTest"); - thread.start(); - mService.loadRebootEscrowDataIfAvailable(new Handler(thread.getLooper())); + mService.loadRebootEscrowDataIfAvailable(mHandler); // Sleep 5s for the retry to complete Thread.sleep(5 * 1000); assertFalse(metricsSuccessCaptor.getValue()); - assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK), + assertEquals( + Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK), metricsErrorCodeCaptor.getValue()); } @@ -645,16 +706,22 @@ public class RebootEscrowManagerTests { // pretend reboot happens here when(mInjected.getBootCount()).thenReturn(1); ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); - doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), - anyInt(), anyInt(), eq(2) /* attempt count */, anyInt(), anyInt(), anyInt()); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + anyInt(), + anyInt(), + eq(2) /* attempt count */, + anyInt(), + anyInt(), + anyInt()); when(mServiceConnection.unwrap(any(), anyLong())) .thenThrow(new IOException()) .thenAnswer(invocation -> invocation.getArgument(0)); - HandlerThread thread = new HandlerThread("RebootEscrowManagerTest"); - thread.start(); - mService.loadRebootEscrowDataIfAvailable(new Handler(thread.getLooper())); + mService.loadRebootEscrowDataIfAvailable(mHandler); // Sleep 5s for the retry to complete Thread.sleep(5 * 1000); verify(mServiceConnection, times(2)).unwrap(any(), anyLong()); @@ -663,6 +730,447 @@ public class RebootEscrowManagerTests { } @Test + public void loadRebootEscrowDataIfAvailable_serverBasedWaitForInternet_success() + throws Exception { + setServerBasedRebootEscrowProvider(); + mMockInjector.setWaitForNetwork(true); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + eq(0) /* error code */, + eq(2) /* Server based */, + eq(1) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); + + // load escrow data + when(mServiceConnection.unwrap(any(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + Network mockNetwork = mock(Network.class); + mMockInjector.mNetworkConsumer = + (callback) -> { + callback.onAvailable(mockNetwork); + }; + + mService.loadRebootEscrowDataIfAvailable(mHandler); + verify(mServiceConnection).unwrap(any(), anyLong()); + assertTrue(metricsSuccessCaptor.getValue()); + verify(mKeyStoreManager).clearKeyStoreEncryptionKey(); + assertNull(mMockInjector.mNetworkCallback); + } + + @Test + public void loadRebootEscrowDataIfAvailable_serverBasedWaitForInternetRemoteException_Failure() + throws Exception { + setServerBasedRebootEscrowProvider(); + mMockInjector.setWaitForNetwork(true); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), + eq(2) /* Server based */, + eq(1) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); + + // load escrow data + when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(RemoteException.class); + Network mockNetwork = mock(Network.class); + mMockInjector.mNetworkConsumer = + (callback) -> { + callback.onAvailable(mockNetwork); + }; + + mService.loadRebootEscrowDataIfAvailable(mHandler); + verify(mServiceConnection).unwrap(any(), anyLong()); + assertFalse(metricsSuccessCaptor.getValue()); + assertEquals( + Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY), + metricsErrorCodeCaptor.getValue()); + assertNull(mMockInjector.mNetworkCallback); + } + + @Test + public void loadRebootEscrowDataIfAvailable_waitForInternet_networkUnavailable() + throws Exception { + setServerBasedRebootEscrowProvider(); + mMockInjector.setWaitForNetwork(true); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), + eq(2) /* Server based */, + eq(0) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); + + // Network is not available within timeout. + mMockInjector.mNetworkConsumer = ConnectivityManager.NetworkCallback::onUnavailable; + mService.loadRebootEscrowDataIfAvailable(mHandler); + assertFalse(metricsSuccessCaptor.getValue()); + assertEquals( + Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK), + metricsErrorCodeCaptor.getValue()); + assertNull(mMockInjector.mNetworkCallback); + } + + @Test + public void loadRebootEscrowDataIfAvailable_waitForInternet_networkLost() throws Exception { + setServerBasedRebootEscrowProvider(); + mMockInjector.setWaitForNetwork(true); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), + eq(2) /* Server based */, + eq(2) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); + + // Network is available, then lost. + when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(new IOException()); + Network mockNetwork = mock(Network.class); + mMockInjector.mNetworkConsumer = + (callback) -> { + callback.onAvailable(mockNetwork); + callback.onLost(mockNetwork); + }; + mService.loadRebootEscrowDataIfAvailable(mHandler); + // Sleep 5s for the retry to complete + Thread.sleep(5 * 1000); + assertFalse(metricsSuccessCaptor.getValue()); + assertEquals( + Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK), + metricsErrorCodeCaptor.getValue()); + assertNull(mMockInjector.mNetworkCallback); + } + + @Test + public void loadRebootEscrowDataIfAvailable_waitForInternet_networkAvailableWithDelay() + throws Exception { + setServerBasedRebootEscrowProvider(); + mMockInjector.setWaitForNetwork(true); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), + eq(2) /* Server based */, + eq(1) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); + + // load escrow data + when(mServiceConnection.unwrap(any(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + // network available after 1 sec + Network mockNetwork = mock(Network.class); + mMockInjector.mNetworkConsumer = + (callback) -> { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + callback.onAvailable(mockNetwork); + }; + mService.loadRebootEscrowDataIfAvailable(mHandler); + verify(mServiceConnection).unwrap(any(), anyLong()); + assertTrue(metricsSuccessCaptor.getValue()); + verify(mKeyStoreManager).clearKeyStoreEncryptionKey(); + assertNull(mMockInjector.mNetworkCallback); + } + + @Test + public void loadRebootEscrowDataIfAvailable_waitForInternet_timeoutExhausted() + throws Exception { + setServerBasedRebootEscrowProvider(); + mMockInjector.setWaitForNetwork(true); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), + eq(2) /* Server based */, + eq(1) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); + + // load reboot escrow data + when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(IOException.class); + Network mockNetwork = mock(Network.class); + // wait past timeout + mMockInjector.mNetworkConsumer = + (callback) -> { + try { + Thread.sleep(3500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + callback.onAvailable(mockNetwork); + }; + mService.loadRebootEscrowDataIfAvailable(mHandler); + verify(mServiceConnection).unwrap(any(), anyLong()); + assertFalse(metricsSuccessCaptor.getValue()); + assertEquals( + Integer.valueOf(RebootEscrowManager.ERROR_TIMEOUT_EXHAUSTED), + metricsErrorCodeCaptor.getValue()); + assertNull(mMockInjector.mNetworkCallback); + } + + @Test + public void loadRebootEscrowDataIfAvailable_serverBasedWaitForNetwork_retryCountExhausted() + throws Exception { + setServerBasedRebootEscrowProvider(); + mMockInjector.setWaitForNetwork(true); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), + eq(2) /* Server based */, + eq(2) /* attempt count */, + anyInt(), + eq(0) /* vbmeta status */, + anyInt()); + + when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(new IOException()); + Network mockNetwork = mock(Network.class); + mMockInjector.mNetworkConsumer = + (callback) -> { + callback.onAvailable(mockNetwork); + }; + + mService.loadRebootEscrowDataIfAvailable(mHandler); + // Sleep 5s for the retry to complete + Thread.sleep(5 * 1000); + verify(mServiceConnection, times(2)).unwrap(any(), anyLong()); + assertFalse(metricsSuccessCaptor.getValue()); + assertEquals( + Integer.valueOf(RebootEscrowManager.ERROR_RETRY_COUNT_EXHAUSTED), + metricsErrorCodeCaptor.getValue()); + assertNull(mMockInjector.mNetworkCallback); + } + + @Test + public void loadRebootEscrowDataIfAvailable_ServerBasedWaitForInternet_RetrySuccess() + throws Exception { + setServerBasedRebootEscrowProvider(); + mMockInjector.setWaitForNetwork(true); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + doNothing() + .when(mInjected) + .reportMetric( + metricsSuccessCaptor.capture(), + anyInt(), + anyInt(), + eq(2) /* attempt count */, + anyInt(), + anyInt(), + anyInt()); + + when(mServiceConnection.unwrap(any(), anyLong())) + .thenThrow(new IOException()) + .thenAnswer(invocation -> invocation.getArgument(0)); + Network mockNetwork = mock(Network.class); + mMockInjector.mNetworkConsumer = + (callback) -> { + callback.onAvailable(mockNetwork); + }; + + mService.loadRebootEscrowDataIfAvailable(mHandler); + // Sleep 5s for the retry to complete + Thread.sleep(5 * 1000); + verify(mServiceConnection, times(2)).unwrap(any(), anyLong()); + assertTrue(metricsSuccessCaptor.getValue()); + verify(mKeyStoreManager).clearKeyStoreEncryptionKey(); + assertNull(mMockInjector.mNetworkCallback); + } + + @Test public void loadRebootEscrowDataIfAvailable_TooManyBootsInBetween_NoMetrics() throws Exception { when(mInjected.getBootCount()).thenReturn(0); @@ -687,7 +1195,7 @@ public class RebootEscrowManagerTests { when(mInjected.getBootCount()).thenReturn(10); when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]); - mService.loadRebootEscrowDataIfAvailable(null); + mService.loadRebootEscrowDataIfAvailable(mHandler); verify(mRebootEscrow).retrieveKey(); verify(mInjected, never()).reportMetric(anyBoolean(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt()); @@ -715,7 +1223,7 @@ public class RebootEscrowManagerTests { when(mInjected.getBootCount()).thenReturn(10); when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]); - mService.loadRebootEscrowDataIfAvailable(null); + mService.loadRebootEscrowDataIfAvailable(mHandler); verify(mInjected, never()).reportMetric(anyBoolean(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt()); } @@ -753,7 +1261,7 @@ public class RebootEscrowManagerTests { // Trigger a vbmeta digest mismatch mStorage.setString(RebootEscrowManager.REBOOT_ESCROW_KEY_VBMETA_DIGEST, "non sense value", USER_SYSTEM); - mService.loadRebootEscrowDataIfAvailable(null); + mService.loadRebootEscrowDataIfAvailable(mHandler); verify(mInjected).reportMetric(eq(true), eq(0) /* error code */, eq(1) /* HAL based */, eq(1) /* attempt count */, anyInt(), eq(2) /* vbmeta status */, anyInt()); assertEquals(mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_KEY_VBMETA_DIGEST, @@ -790,7 +1298,7 @@ public class RebootEscrowManagerTests { eq(1) /* attempt count */, anyInt(), anyInt(), anyInt()); when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> null); - mService.loadRebootEscrowDataIfAvailable(null); + mService.loadRebootEscrowDataIfAvailable(mHandler); verify(mRebootEscrow).retrieveKey(); assertFalse(metricsSuccessCaptor.getValue()); assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY), 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 c5131c8f8c1d..57e403c21ac8 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 @@ public class NotificationListenersTest extends UiServiceTestCase { @Test public void testNotifyPostedLockedInLockdownMode() { - NotificationRecord r = mock(NotificationRecord.class); - NotificationRecord old = mock(NotificationRecord.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(); - - // 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(); - } - - @Test - public void testnotifyRankingUpdateLockedInLockdownMode() { - List chn = mock(List.class); - - // before the lockdown mode - when(mNm.isInLockDownMode()).thenReturn(false); - mListeners.notifyRankingUpdateLocked(chn); - verify(chn, atLeast(1)).size(); - - // in the lockdown mode - reset(chn); - when(mNm.isInLockDownMode()).thenReturn(true); - mListeners.notifyRankingUpdateLocked(chn); - verify(chn, never()).size(); + NotificationRecord r0 = mock(NotificationRecord.class); + NotificationRecord old0 = mock(NotificationRecord.class); + UserHandle uh0 = mock(UserHandle.class); + + NotificationRecord r1 = mock(NotificationRecord.class); + NotificationRecord old1 = mock(NotificationRecord.class); + UserHandle uh1 = mock(UserHandle.class); + + // 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(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, atLeast(2)).getSbn(); + + 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(); - - // 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(); + // 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); + + 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 4d1c78643f6d..dd43a6b0f1e0 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.Adjustment; 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // 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 b49e5cbfa9dc..8cf74fbf88b7 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 @@ package com.android.server.notification; 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 @@ public class TestableNotificationManagerService extends NotificationManagerServi @Nullable NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback; + @Nullable + Boolean mIsVisibleToListenerReturnValue = null; + TestableNotificationManagerService(Context context, NotificationRecordLogger logger, InstanceIdSequence notificationInstanceIdSequence) { super(context, logger, notificationInstanceIdSequence); @@ -119,6 +124,19 @@ public class TestableNotificationManagerService extends NotificationManagerServi 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 2ccdcaace8bf..4550b56f6fd0 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @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 adf694c2a88d..0462e1be7a5f 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.Intent.FLAG_ACTIVITY_NEW_TASK; 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 @@ public class RecentTasksTest extends WindowTestsBase { @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 @@ public class RecentTasksTest extends WindowTestsBase { 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 @@ public class RecentTasksTest extends WindowTestsBase { // 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 13550f02266b..f373f6ece5ad 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -478,6 +478,14 @@ public class SubscriptionManager { 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 3379ce5e7739..b32f04633247 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -1109,6 +1109,7 @@ public class ApnSetting implements Parcelable { 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 @@ public class ApnSetting implements Parcelable { 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 b51e8d3d3c5d..7a5bf067f05f 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 @@ public class SmsMessage extends SmsMessageBase { ENCODING_UNKNOWN, 0, 0); } - /** * Gets an SMS-SUBMIT PDU for a destination address and a message using the specified encoding. * @@ -272,7 +271,7 @@ public class SmsMessage extends SmsMessageBase { 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 @@ public class SmsMessage extends SmsMessageBase { 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 @@ public class SmsMessage extends SmsMessageBase { 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 @@ public class SmsMessage extends SmsMessageBase { 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 @@ public class SmsMessage extends SmsMessageBase { * @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 @@ public class SmsMessage extends SmsMessageBase { 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 @@ public class SmsMessage extends SmsMessageBase { } /** + * 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 @@ public class SmsMessage extends SmsMessageBase { 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 @@ public class SmsMessage extends SmsMessageBase { 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 000000000000..27e16681899e --- /dev/null +++ b/tests/VectorDrawableTest/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 24939 + +include /graphics/java/android/graphics/OWNERS |