Merge changes from topic "nscobie-hwui_unit_tests"
* changes:
Tweak GraphicsStats#findRootPath to accept various valid locations
Enable hwui_unit_tests in presubmit for libs/hwui
Fix GraphicsStats#findRootPath and disable CacheManager#trimMemory tests
Change hwui_unit_tests (and benchmarks) to push to non-root writable dir
diff --git a/BROADCASTS_OWNERS b/BROADCASTS_OWNERS
new file mode 100644
index 0000000..1ea610b
--- /dev/null
+++ b/BROADCASTS_OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 316181
+ctate@android.com
+jsharkey@android.com
+jsharkey@google.com
+sudheersai@google.com
+yamasani@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/core/api/current.txt b/core/api/current.txt
index ff9c7b1..d132706 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17088,8 +17088,8 @@
method public boolean flush(android.hardware.SensorEventListener);
method public static float getAltitude(float, float);
method public static void getAngleChange(float[], float[], float[]);
- method public android.hardware.Sensor getDefaultSensor(int);
- method public android.hardware.Sensor getDefaultSensor(int, boolean);
+ method @Nullable public android.hardware.Sensor getDefaultSensor(int);
+ method @Nullable public android.hardware.Sensor getDefaultSensor(int, boolean);
method public java.util.List<android.hardware.Sensor> getDynamicSensorList(int);
method public static float getInclination(float[]);
method public static float[] getOrientation(float[], float[]);
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index 1c9713d..2cde539 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -13,3 +13,4 @@
per-file LocusId* = file:/core/java/android/service/contentcapture/OWNERS
per-file ComponentCallbacksController = file:/services/core/java/com/android/server/wm/OWNERS
per-file ComponentCallbacksController = charlesccchen@google.com
+per-file Broadcast* = file:/BROADCASTS_OWNERS
diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS
index 3b6a564..d2a2f12 100644
--- a/core/java/android/hardware/OWNERS
+++ b/core/java/android/hardware/OWNERS
@@ -16,3 +16,6 @@
# Buffers
per-file HardwareBuffer* = file:/graphics/java/android/graphics/OWNERS
per-file DataSpace* = file:/graphics/java/android/graphics/OWNERS
+
+# OverlayProperties
+per-file OverlayProperties* = file:/graphics/java/android/graphics/OWNERS
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index dec424c..6d8c4a9 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -16,6 +16,7 @@
package android.hardware;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.compat.annotation.UnsupportedAppUsage;
@@ -496,7 +497,7 @@
* @see #getSensorList(int)
* @see Sensor
*/
- public Sensor getDefaultSensor(int type) {
+ public @Nullable Sensor getDefaultSensor(int type) {
// TODO: need to be smarter, for now, just return the 1st sensor
List<Sensor> l = getSensorList(type);
boolean wakeUpSensor = false;
@@ -544,7 +545,7 @@
* and the application has the necessary permissions, or null otherwise.
* @see Sensor#isWakeUpSensor()
*/
- public Sensor getDefaultSensor(int type, boolean wakeUp) {
+ public @Nullable Sensor getDefaultSensor(int type, boolean wakeUp) {
List<Sensor> l = getSensorList(type);
for (Sensor sensor : l) {
if (sensor.isWakeUpSensor() == wakeUp) {
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 468e604..5abe677 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.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 @@
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 @@
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 60d8cac..7c2e518 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -108,6 +108,34 @@
}
/**
+ * This is meant to be called by UsbRequest's queue() in order to synchronize on
+ * UsbDeviceConnection's mLock to prevent the connection being closed while queueing.
+ */
+ /* package */ boolean queueRequest(UsbRequest request, ByteBuffer buffer, int length) {
+ synchronized (mLock) {
+ if (!isOpen()) {
+ return false;
+ }
+
+ return request.queueIfConnectionOpen(buffer, length);
+ }
+ }
+
+ /**
+ * This is meant to be called by UsbRequest's queue() in order to synchronize on
+ * UsbDeviceConnection's mLock to prevent the connection being closed while queueing.
+ */
+ /* package */ boolean queueRequest(UsbRequest request, @Nullable ByteBuffer buffer) {
+ synchronized (mLock) {
+ if (!isOpen()) {
+ return false;
+ }
+
+ return request.queueIfConnectionOpen(buffer);
+ }
+ }
+
+ /**
* Releases all system resources related to the device.
* Once the object is closed it cannot be used again.
* The client must call {@link UsbManager#openDevice} again
diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java
index 6ac5e8d..beb0f8d 100644
--- a/core/java/android/hardware/usb/UsbRequest.java
+++ b/core/java/android/hardware/usb/UsbRequest.java
@@ -113,11 +113,13 @@
* Releases all resources related to this request.
*/
public void close() {
- if (mNativeContext != 0) {
- mEndpoint = null;
- mConnection = null;
- native_close();
- mCloseGuard.close();
+ synchronized (mLock) {
+ if (mNativeContext != 0) {
+ mEndpoint = null;
+ mConnection = null;
+ native_close();
+ mCloseGuard.close();
+ }
}
}
@@ -191,10 +193,32 @@
*/
@Deprecated
public boolean queue(ByteBuffer buffer, int length) {
+ UsbDeviceConnection connection = mConnection;
+ if (connection == null) {
+ // The expected exception by CTS Verifier - USB Device test
+ throw new NullPointerException("invalid connection");
+ }
+
+ // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent
+ // the connection being closed while queueing.
+ return connection.queueRequest(this, buffer, length);
+ }
+
+ /**
+ * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over
+ * there, to prevent the connection being closed while queueing.
+ */
+ /* package */ boolean queueIfConnectionOpen(ByteBuffer buffer, int length) {
+ UsbDeviceConnection connection = mConnection;
+ if (connection == null || !connection.isOpen()) {
+ // The expected exception by CTS Verifier - USB Device test
+ throw new NullPointerException("invalid connection");
+ }
+
boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
boolean result;
- if (mConnection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
+ if (connection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
&& length > MAX_USBFS_BUFFER_SIZE) {
length = MAX_USBFS_BUFFER_SIZE;
}
@@ -243,6 +267,28 @@
* @return true if the queueing operation succeeded
*/
public boolean queue(@Nullable ByteBuffer buffer) {
+ UsbDeviceConnection connection = mConnection;
+ if (connection == null) {
+ // The expected exception by CTS Verifier - USB Device test
+ throw new IllegalStateException("invalid connection");
+ }
+
+ // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent
+ // the connection being closed while queueing.
+ return connection.queueRequest(this, buffer);
+ }
+
+ /**
+ * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over
+ * there, to prevent the connection being closed while queueing.
+ */
+ /* package */ boolean queueIfConnectionOpen(@Nullable ByteBuffer buffer) {
+ UsbDeviceConnection connection = mConnection;
+ if (connection == null || !connection.isOpen()) {
+ // The expected exception by CTS Verifier - USB Device test
+ throw new IllegalStateException("invalid connection");
+ }
+
// Request need to be initialized
Preconditions.checkState(mNativeContext != 0, "request is not initialized");
@@ -260,7 +306,7 @@
mIsUsingNewQueue = true;
wasQueued = native_queue(null, 0, 0);
} else {
- if (mConnection.getContext().getApplicationInfo().targetSdkVersion
+ if (connection.getContext().getApplicationInfo().targetSdkVersion
< Build.VERSION_CODES.P) {
// Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once
Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE,
@@ -363,11 +409,12 @@
* @return true if cancelling succeeded
*/
public boolean cancel() {
- if (mConnection == null) {
+ UsbDeviceConnection connection = mConnection;
+ if (connection == null) {
return false;
}
- return mConnection.cancelRequest(this);
+ return connection.cancelRequest(this);
}
/**
@@ -382,7 +429,8 @@
* @return true if cancelling succeeded.
*/
/* package */ boolean cancelIfOpen() {
- if (mNativeContext == 0 || (mConnection != null && !mConnection.isOpen())) {
+ UsbDeviceConnection connection = mConnection;
+ if (mNativeContext == 0 || (connection != null && !connection.isOpen())) {
Log.w(TAG,
"Detected attempt to cancel a request on a connection which isn't open");
return false;
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 935374d1..911aaf3 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -122,7 +122,7 @@
Binder token;
public NfcActivityState(Activity activity) {
- if (activity.getWindow().isDestroyed()) {
+ if (activity.isDestroyed()) {
throw new IllegalStateException("activity is already destroyed");
}
// Check if activity is resumed right now, as we will not
diff --git a/core/java/android/nfc/TechListParcel.java b/core/java/android/nfc/TechListParcel.java
index 90645dd..9f01559 100644
--- a/core/java/android/nfc/TechListParcel.java
+++ b/core/java/android/nfc/TechListParcel.java
@@ -53,7 +53,7 @@
int count = source.readInt();
String[][] techLists = new String[count][];
for (int i = 0; i < count; i++) {
- techLists[i] = source.readStringArray();
+ techLists[i] = source.createStringArray();
}
return new TechListParcel(techLists);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7db6130..b377c92 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3344,9 +3344,26 @@
}
}
- // Fetch all flags for the namespace at once for caching purposes
- Bundle b = cp.call(cr.getAttributionSource(),
- mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
+ Bundle b;
+ // b/252663068: if we're in system server and the caller did not call
+ // clearCallingIdentity, the read would fail due to mismatched AttributionSources.
+ // TODO(b/256013480): remove this bypass after fixing the callers in system server.
+ if (namespace.equals(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER)
+ && Settings.isInSystemServer()
+ && Binder.getCallingUid() != Process.myUid()) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Fetch all flags for the namespace at once for caching purposes
+ b = cp.call(cr.getAttributionSource(),
+ mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } else {
+ // Fetch all flags for the namespace at once for caching purposes
+ b = cp.call(cr.getAttributionSource(),
+ mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
+ }
if (b == null) {
// Invalid response, return an empty map
return keyValues;
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 5ff9263..ac46be7 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4803,6 +4803,13 @@
"phone_number_source_ims";
/**
+ * TelephonyProvider column name for last used TP - message Reference
+ *
+ * @hide
+ */
+ public static final String COLUMN_TP_MESSAGE_REF = "tp_message_ref";
+
+ /**
* TelephonyProvider column name for the device's preferred usage setting.
*
* @hide
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index 5f4a9cd..473134e 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -172,14 +172,14 @@
@Override
public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
- prepareIntentForCrossProfileLaunch(mResolvedIntent, userId);
+ TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, userId);
activity.startActivityAsCaller(mResolvedIntent, options, false, userId);
return true;
}
@Override
public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
- prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier());
+ TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier());
activity.startActivityAsUser(mResolvedIntent, options, user);
return false;
}
@@ -224,13 +224,6 @@
}
};
- private static void prepareIntentForCrossProfileLaunch(Intent intent, int targetUserId) {
- final int currentUserId = UserHandle.myUserId();
- if (targetUserId != currentUserId) {
- intent.fixUris(currentUserId);
- }
- }
-
private DisplayResolveInfo(Parcel in) {
mDisplayLabel = in.readCharSequence();
mExtendedInfo = in.readCharSequence();
diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
index 264e4f7..4b9b7cb 100644
--- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
@@ -232,6 +232,7 @@
}
intent.setComponent(mChooserTarget.getComponentName());
intent.putExtras(mChooserTarget.getIntentExtras());
+ TargetInfo.prepareIntentForCrossProfileLaunch(intent, userId);
// Important: we will ignore the target security checks in ActivityManager
// if and only if the ChooserTarget's target package is the same package
diff --git a/core/java/com/android/internal/app/chooser/TargetInfo.java b/core/java/com/android/internal/app/chooser/TargetInfo.java
index f56ab17..7bb7ddc 100644
--- a/core/java/com/android/internal/app/chooser/TargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/TargetInfo.java
@@ -130,4 +130,15 @@
* @return true if this target should be pinned to the front by the request of the user
*/
boolean isPinned();
+
+ /**
+ * Fix the URIs in {@code intent} if cross-profile sharing is required. This should be called
+ * before launching the intent as another user.
+ */
+ static void prepareIntentForCrossProfileLaunch(Intent intent, int targetUserId) {
+ final int currentUserId = UserHandle.myUserId();
+ if (targetUserId != currentUserId) {
+ intent.fixUris(currentUserId);
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/hardware/camera2/OWNERS b/core/tests/coretests/src/android/hardware/camera2/OWNERS
new file mode 100644
index 0000000..f48a95c
--- /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 0000000..a38c040
--- /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 335cea1..c74600b 100644
--- a/core/tests/nfctests/Android.bp
+++ b/core/tests/nfctests/Android.bp
@@ -27,6 +27,7 @@
"androidx.test.ext.junit",
"androidx.test.rules",
"mockito-target-minus-junit4",
+ "truth-prebuilt",
],
libs: [
"android.test.runner",
diff --git a/core/tests/nfctests/src/android/nfc/TechListParcelTest.java b/core/tests/nfctests/src/android/nfc/TechListParcelTest.java
new file mode 100644
index 0000000..a12bbbc
--- /dev/null
+++ b/core/tests/nfctests/src/android/nfc/TechListParcelTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public class TechListParcelTest {
+
+ private static final String[] TECH_LIST_1 = new String[] { "tech1.1", "tech1.2" };
+ private static final String[] TECH_LIST_2 = new String[] { "tech2.1" };
+ private static final String[] TECH_LIST_EMPTY = new String[] {};
+
+ @Test
+ public void testWriteParcel() {
+ TechListParcel techListParcel = new TechListParcel(TECH_LIST_1, TECH_LIST_2);
+
+ Parcel parcel = Parcel.obtain();
+ techListParcel.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ TechListParcel actualTechList =
+ TechListParcel.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ assertThat(actualTechList.getTechLists().length).isEqualTo(2);
+ assertThat(Arrays.equals(actualTechList.getTechLists()[0], TECH_LIST_1)).isTrue();
+ assertThat(Arrays.equals(actualTechList.getTechLists()[1], TECH_LIST_2)).isTrue();
+ }
+
+ @Test
+ public void testWriteParcelArrayEmpty() {
+ TechListParcel techListParcel = new TechListParcel();
+
+ Parcel parcel = Parcel.obtain();
+ techListParcel.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ TechListParcel actualTechList =
+ TechListParcel.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ assertThat(actualTechList.getTechLists().length).isEqualTo(0);
+ }
+
+ @Test
+ public void testWriteParcelElementEmpty() {
+ TechListParcel techListParcel = new TechListParcel(TECH_LIST_EMPTY);
+
+ Parcel parcel = Parcel.obtain();
+ techListParcel.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ TechListParcel actualTechList =
+ TechListParcel.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ assertThat(actualTechList.getTechLists().length).isEqualTo(1);
+ assertThat(Arrays.equals(actualTechList.getTechLists()[0], TECH_LIST_EMPTY)).isTrue();
+ }
+
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 8ca1607..ff42fb5 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -342,10 +342,6 @@
<permission name="android.permission.SET_WALLPAPER_COMPONENT" />
<permission name="android.permission.SET_WALLPAPER_DIM_AMOUNT" />
<permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
- <!-- Permission required for CTS test - TrustTestCases -->
- <permission name="android.permission.PROVIDE_TRUST_AGENT" />
- <permission name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
- <permission name="android.permission.TRUST_LISTENER" />
<!-- Permissions required for Incremental CTS tests -->
<permission name="com.android.permission.USE_INSTALLER_V2"/>
<permission name="android.permission.LOADER_USAGE_STATS"/>
diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp
index ff1714d..a996700 100644
--- a/libs/incident/Android.bp
+++ b/libs/incident/Android.bp
@@ -60,6 +60,7 @@
":libincident_aidl",
"src/IncidentReportArgs.cpp",
],
+ min_sdk_version: "29",
}
cc_library_shared {
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index 2f1a36c..4957300 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -255,10 +255,10 @@
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/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 845975d..c8187b8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -35,7 +35,6 @@
import android.os.ParcelUuid;
import android.os.SystemClock;
import android.text.TextUtils;
-import android.util.EventLog;
import android.util.Log;
import android.util.LruCache;
import android.util.Pair;
@@ -1020,14 +1019,7 @@
if (BluetoothUuid.containsAnyUuid(uuids, PbapServerProfile.PBAB_CLIENT_UUIDS)) {
// The pairing dialog now warns of phone-book access for paired devices.
// No separate prompt is displayed after pairing.
- if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_UNKNOWN) {
- if (BluetoothUtils.isDeviceClassMatched(mDevice,
- BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE)
- || BluetoothUtils.isDeviceClassMatched(mDevice,
- BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)) {
- EventLog.writeEvent(0x534e4554, "138529441", -1, "");
- }
- }
+ mDevice.getPhonebookAccessPermission();
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index fd7554f..cd667ca 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -48,6 +48,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -376,9 +377,11 @@
Setting newSetting = new Setting(name, oldSetting.getValue(), null,
oldSetting.getPackageName(), oldSetting.getTag(), false,
oldSetting.getId());
+ int newSize = getNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), 0,
+ oldValue, newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue());
+ checkNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize);
mSettings.put(name, newSetting);
- updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue,
- newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue());
+ updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize);
scheduleWriteIfNeededLocked();
}
}
@@ -410,6 +413,13 @@
Setting oldState = mSettings.get(name);
String oldValue = (oldState != null) ? oldState.value : null;
String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null;
+ String newDefaultValue = makeDefault ? value : oldDefaultValue;
+
+ int newSize = getNewMemoryUsagePerPackageLocked(packageName,
+ oldValue == null ? name.length() : 0 /* deltaKeySize */,
+ oldValue, value, oldDefaultValue, newDefaultValue);
+ checkNewMemoryUsagePerPackageLocked(packageName, newSize);
+
Setting newState;
if (oldState != null) {
@@ -430,8 +440,7 @@
addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState);
- updateMemoryUsagePerPackageLocked(packageName, oldValue, value,
- oldDefaultValue, newState.getDefaultValue());
+ updateMemoryUsagePerPackageLocked(packageName, newSize);
scheduleWriteIfNeededLocked();
@@ -552,13 +561,18 @@
}
Setting oldState = mSettings.remove(name);
+ if (oldState == null) {
+ return false;
+ }
+ int newSize = getNewMemoryUsagePerPackageLocked(oldState.packageName,
+ -name.length() /* deltaKeySize */,
+ oldState.value, null, oldState.defaultValue, null);
FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, /* value= */ "",
/* newValue= */ "", oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED);
- updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value,
- null, oldState.defaultValue, null);
+ updateMemoryUsagePerPackageLocked(oldState.packageName, newSize);
addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState);
@@ -575,20 +589,23 @@
}
Setting setting = mSettings.get(name);
+ if (setting == null) {
+ return false;
+ }
Setting oldSetting = new Setting(setting);
String oldValue = setting.getValue();
String oldDefaultValue = setting.getDefaultValue();
+ int newSize = getNewMemoryUsagePerPackageLocked(setting.packageName, 0, oldValue,
+ oldDefaultValue, oldDefaultValue, oldDefaultValue);
+ checkNewMemoryUsagePerPackageLocked(setting.packageName, newSize);
+
if (!setting.reset()) {
return false;
}
- String newValue = setting.getValue();
- String newDefaultValue = setting.getDefaultValue();
-
- updateMemoryUsagePerPackageLocked(setting.packageName, oldValue,
- newValue, oldDefaultValue, newDefaultValue);
+ updateMemoryUsagePerPackageLocked(setting.packageName, newSize);
addHistoricalOperationLocked(HISTORICAL_OPERATION_RESET, oldSetting);
@@ -696,38 +713,49 @@
}
@GuardedBy("mLock")
- private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue,
- String newValue, String oldDefaultValue, String newDefaultValue) {
- if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) {
+ private boolean isExemptFromMemoryUsageCap(String packageName) {
+ return mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED
+ || SYSTEM_PACKAGE_NAME.equals(packageName);
+ }
+
+ @GuardedBy("mLock")
+ private void checkNewMemoryUsagePerPackageLocked(String packageName, int newSize)
+ throws IllegalStateException {
+ if (isExemptFromMemoryUsageCap(packageName)) {
return;
}
-
- if (SYSTEM_PACKAGE_NAME.equals(packageName)) {
- return;
- }
-
- final int oldValueSize = (oldValue != null) ? oldValue.length() : 0;
- final int newValueSize = (newValue != null) ? newValue.length() : 0;
- final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0;
- final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0;
- final int deltaSize = newValueSize + newDefaultValueSize
- - oldValueSize - oldDefaultValueSize;
-
- Integer currentSize = mPackageToMemoryUsage.get(packageName);
- final int newSize = Math.max((currentSize != null)
- ? currentSize + deltaSize : deltaSize, 0);
-
if (newSize > mMaxBytesPerAppPackage) {
throw new IllegalStateException("You are adding too many system settings. "
+ "You should stop using system settings for app specific data"
+ " package: " + packageName);
}
+ }
+ @GuardedBy("mLock")
+ private int getNewMemoryUsagePerPackageLocked(String packageName, int deltaKeySize,
+ String oldValue, String newValue, String oldDefaultValue, String newDefaultValue) {
+ if (isExemptFromMemoryUsageCap(packageName)) {
+ return 0;
+ }
+ final Integer currentSize = mPackageToMemoryUsage.get(packageName);
+ final int oldValueSize = (oldValue != null) ? oldValue.length() : 0;
+ final int newValueSize = (newValue != null) ? newValue.length() : 0;
+ final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0;
+ final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0;
+ final int deltaSize = deltaKeySize + newValueSize + newDefaultValueSize
+ - oldValueSize - oldDefaultValueSize;
+ return Math.max((currentSize != null) ? currentSize + deltaSize : deltaSize, 0);
+ }
+
+ @GuardedBy("mLock")
+ private void updateMemoryUsagePerPackageLocked(String packageName, int newSize) {
+ if (isExemptFromMemoryUsageCap(packageName)) {
+ return;
+ }
if (DEBUG) {
Slog.i(LOG_TAG, "Settings for package: " + packageName
+ " size: " + newSize + " bytes.");
}
-
mPackageToMemoryUsage.put(packageName, newSize);
}
@@ -1556,4 +1584,11 @@
}
return false;
}
+
+ @VisibleForTesting
+ public int getMemoryUsage(String packageName) {
+ synchronized (mLock) {
+ return mPackageToMemoryUsage.getOrDefault(packageName, 0);
+ }
+ }
}
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index 69eb713..f6d4329 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -20,6 +20,8 @@
import android.util.TypedXmlSerializer;
import android.util.Xml;
+import com.google.common.base.Strings;
+
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
@@ -276,4 +278,132 @@
settingsState.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING);
return settingsState;
}
+
+ public void testInsertSetting_memoryUsage() {
+ SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+ // No exception should be thrown when there is no cap
+ settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
+ null, false, "p1");
+ settingsState.deleteSettingLocked(SETTING_NAME);
+
+ settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper());
+ // System package doesn't have memory usage limit
+ settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
+ null, false, SYSTEM_PACKAGE);
+ settingsState.deleteSettingLocked(SETTING_NAME);
+
+ // Should not throw if usage is under the cap
+ settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 19975),
+ null, false, "p1");
+ settingsState.deleteSettingLocked(SETTING_NAME);
+ try {
+ settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
+ null, false, "p1");
+ fail("Should throw because it exceeded per package memory usage");
+ } catch (IllegalStateException ex) {
+ assertTrue(ex.getMessage().contains("p1"));
+ }
+ try {
+ settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
+ null, false, "p1");
+ fail("Should throw because it exceeded per package memory usage");
+ } catch (IllegalStateException ex) {
+ assertTrue(ex.getMessage().contains("p1"));
+ }
+ assertTrue(settingsState.getSettingLocked(SETTING_NAME).isNull());
+ try {
+ settingsState.insertSettingLocked(Strings.repeat("A", 20001), "",
+ null, false, "p1");
+ fail("Should throw because it exceeded per package memory usage");
+ } catch (IllegalStateException ex) {
+ assertTrue(ex.getMessage().contains("You are adding too many system settings"));
+ }
+ }
+
+ public void testMemoryUsagePerPackage() {
+ SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper());
+
+ // Test inserting one key with default
+ final String testKey1 = SETTING_NAME;
+ final String testValue1 = Strings.repeat("A", 100);
+ settingsState.insertSettingLocked(testKey1, testValue1, null, true, TEST_PACKAGE);
+ int expectedMemUsage = testKey1.length() + testValue1.length()
+ + testValue1.length() /* size for default */;
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test inserting another key
+ final String testKey2 = SETTING_NAME + "2";
+ settingsState.insertSettingLocked(testKey2, testValue1, null, false, TEST_PACKAGE);
+ expectedMemUsage += testKey2.length() + testValue1.length();
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test updating first key with new default
+ final String testValue2 = Strings.repeat("A", 300);
+ settingsState.insertSettingLocked(testKey1, testValue2, null, true, TEST_PACKAGE);
+ expectedMemUsage += (testValue2.length() - testValue1.length()) * 2;
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test updating first key without new default
+ final String testValue3 = Strings.repeat("A", 50);
+ settingsState.insertSettingLocked(testKey1, testValue3, null, false, TEST_PACKAGE);
+ expectedMemUsage -= testValue2.length() - testValue3.length();
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test updating second key
+ settingsState.insertSettingLocked(testKey2, testValue2, null, false, TEST_PACKAGE);
+ expectedMemUsage -= testValue1.length() - testValue2.length();
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test resetting key
+ settingsState.resetSettingLocked(testKey1);
+ expectedMemUsage += testValue2.length() - testValue3.length();
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test resetting default value
+ settingsState.resetSettingDefaultValueLocked(testKey1);
+ expectedMemUsage -= testValue2.length();
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test deletion
+ settingsState.deleteSettingLocked(testKey2);
+ expectedMemUsage -= testValue2.length() + testKey2.length() /* key is deleted too */;
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test another package with a different key
+ final String testPackage2 = TEST_PACKAGE + "2";
+ final String testKey3 = SETTING_NAME + "3";
+ settingsState.insertSettingLocked(testKey3, testValue1, null, true, testPackage2);
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+ final int expectedMemUsage2 = testKey3.length() + testValue1.length() * 2;
+ assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2));
+
+ // Test system package
+ settingsState.insertSettingLocked(testKey1, testValue1, null, true, SYSTEM_PACKAGE);
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+ assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2));
+ assertEquals(0, settingsState.getMemoryUsage(SYSTEM_PACKAGE));
+
+ // Test invalid value
+ try {
+ settingsState.insertSettingLocked(testKey1, Strings.repeat("A", 20001), null, false,
+ TEST_PACKAGE);
+ fail("Should throw because it exceeded per package memory usage");
+ } catch (IllegalStateException ex) {
+ assertTrue(ex.getMessage().contains("You are adding too many system settings"));
+ }
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+ // Test invalid key
+ try {
+ settingsState.insertSettingLocked(Strings.repeat("A", 20001), "", null, false,
+ TEST_PACKAGE);
+ fail("Should throw because it exceeded per package memory usage");
+ } catch (IllegalStateException ex) {
+ assertTrue(ex.getMessage().contains("You are adding too many system settings"));
+ }
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+ }
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 979e9ea..f13d961 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -578,11 +578,6 @@
<!-- Permission required for CTS test - PeopleManagerTest -->
<uses-permission android:name="android.permission.READ_PEOPLE_DATA" />
- <!-- Permissions required for CTS test - TrustTestCases -->
- <uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" />
- <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
- <uses-permission android:name="android.permission.TRUST_LISTENER" />
-
<!-- Permission required for CTS test - CtsTaskFpsCallbackTestCases -->
<uses-permission android:name="android.permission.ACCESS_FPS_COUNTER" />
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index ded466a..2727c83 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -23,8 +23,8 @@
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.res.Configuration;
+import android.content.res.Configuration.Orientation;
import android.metrics.LogMaker;
-import android.util.Log;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
@@ -75,6 +75,7 @@
@Nullable
private Consumer<Boolean> mMediaVisibilityChangedListener;
+ @Orientation
private int mLastOrientation;
private String mCachedSpecs = "";
@Nullable
@@ -88,21 +89,16 @@
new QSPanel.OnConfigurationChangedListener() {
@Override
public void onConfigurationChange(Configuration newConfig) {
+ mQSLogger.logOnConfigurationChanged(
+ /* lastOrientation= */ mLastOrientation,
+ /* newOrientation= */ newConfig.orientation,
+ /* containerName= */ mView.getDumpableTag());
+
mShouldUseSplitNotificationShade =
- LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
- // Logging to aid the investigation of b/216244185.
- Log.d(TAG,
- "onConfigurationChange: "
- + "mShouldUseSplitNotificationShade="
- + mShouldUseSplitNotificationShade + ", "
- + "newConfig.windowConfiguration="
- + newConfig.windowConfiguration);
- mQSLogger.logOnConfigurationChanged(mLastOrientation, newConfig.orientation,
- mView.getDumpableTag());
- if (newConfig.orientation != mLastOrientation) {
- mLastOrientation = newConfig.orientation;
- switchTileLayout(false);
- }
+ LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
+ mLastOrientation = newConfig.orientation;
+
+ switchTileLayoutIfNeeded();
onConfigurationChanged();
}
};
@@ -334,6 +330,10 @@
}
}
+ private void switchTileLayoutIfNeeded() {
+ switchTileLayout(/* force= */ false);
+ }
+
boolean switchTileLayout(boolean force) {
/* Whether or not the panel currently contains a media player. */
boolean horizontal = shouldUseHorizontalLayout();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
index 76025ab7..0446165 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 @@
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 3cad2a0..b847ad0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -277,7 +277,7 @@
// Then the layout changes
assertThat(mController.shouldUseHorizontalLayout()).isTrue();
- verify(mHorizontalLayoutListener).run(); // not invoked
+ verify(mHorizontalLayoutListener).run();
// When it is rotated back to portrait
mConfiguration.orientation = Configuration.ORIENTATION_PORTRAIT;
@@ -300,4 +300,24 @@
verify(mQSTile).refreshState();
verify(mOtherTile, never()).refreshState();
}
+
+ @Test
+ public void configurationChange_onlySplitShadeConfigChanges_horizontalLayoutStatusUpdated() {
+ // Preconditions for horizontal layout
+ when(mMediaHost.getVisible()).thenReturn(true);
+ when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(false);
+ mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ mController.setUsingHorizontalLayoutChangeListener(mHorizontalLayoutListener);
+ mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration);
+ assertThat(mController.shouldUseHorizontalLayout()).isTrue();
+ reset(mHorizontalLayoutListener);
+
+ // Only split shade status changes
+ when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
+ mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration);
+
+ // Horizontal layout is updated accordingly.
+ assertThat(mController.shouldUseHorizontalLayout()).isFalse();
+ verify(mHorizontalLayoutListener).run();
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2a4bcb0..1e9c3b7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -657,25 +657,27 @@
userState.mBindingServices.removeIf(filter);
userState.mCrashedServices.removeIf(filter);
final Iterator<ComponentName> it = userState.mEnabledServices.iterator();
+ boolean anyServiceRemoved = false;
while (it.hasNext()) {
final ComponentName comp = it.next();
final String compPkg = comp.getPackageName();
if (compPkg.equals(packageName)) {
it.remove();
- // Update the enabled services setting.
- persistComponentNamesToSettingLocked(
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- userState.mEnabledServices, userId);
- // Update the touch exploration granted services setting.
userState.mTouchExplorationGrantedServices.remove(comp);
- persistComponentNamesToSettingLocked(
- Settings.Secure.
- TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
- userState.mTouchExplorationGrantedServices, userId);
- onUserStateChangedLocked(userState);
- return;
+ anyServiceRemoved = true;
}
}
+ if (anyServiceRemoved) {
+ // Update the enabled services setting.
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ userState.mEnabledServices, userId);
+ // Update the touch exploration granted services setting.
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
+ userState.mTouchExplorationGrantedServices, userId);
+ onUserStateChangedLocked(userState);
+ }
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 38237fa..34b7a8a 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -573,12 +573,6 @@
}
}
- if (onClickIntent != null) {
- views.setOnClickPendingIntent(android.R.id.background,
- PendingIntent.getActivity(mContext, 0, onClickIntent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
- }
-
Icon icon = appInfo.icon != 0
? Icon.createWithResource(appInfo.packageName, appInfo.icon)
: Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
@@ -590,6 +584,12 @@
for (int j = 0; j < widgetCount; j++) {
Widget widget = provider.widgets.get(j);
if (targetWidget != null && targetWidget != widget) continue;
+ if (onClickIntent != null) {
+ views.setOnClickPendingIntent(android.R.id.background,
+ PendingIntent.getActivity(mContext, widget.appWidgetId, onClickIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT
+ | PendingIntent.FLAG_IMMUTABLE));
+ }
if (widget.replaceWithMaskedViewsLocked(views)) {
scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 9268fc0..bf71e6b 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -154,8 +154,8 @@
"android.hardware.health-translate-java",
"android.hardware.light-V1-java",
"android.hardware.tv.cec-V1.1-java",
- "android.hardware.tv.cec-V1-java",
- "android.hardware.tv.hdmi-V1-java",
+ "android.hardware.tv.hdmi.cec-V1-java",
+ "android.hardware.tv.hdmi.connection-V1-java",
"android.hardware.weaver-V1.0-java",
"android.hardware.biometrics.face-V1.0-java",
"android.hardware.biometrics.fingerprint-V2.3-java",
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index da209f0..5cdcd42 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -41,3 +41,5 @@
# Multiuser
per-file User* = file:/MULTIUSER_OWNERS
+# Broadcasts
+per-file Broadcast* = file:/BROADCASTS_OWNERS
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index bda60ff..8624ee0 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -379,11 +379,16 @@
resolvedType = key.requestResolvedType;
}
- // Apply any launch flags from the ActivityOptions. This is to ensure that the caller
- // can specify a consistent launch mode even if the PendingIntent is immutable
+ // Apply any launch flags from the ActivityOptions. This is used only by SystemUI
+ // to ensure that we can launch the pending intent with a consistent launch mode even
+ // if the provided PendingIntent is immutable (ie. to force an activity to launch into
+ // a new task, or to launch multiple instances if supported by the app)
final ActivityOptions opts = ActivityOptions.fromBundle(options);
if (opts != null) {
- finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
+ // TODO(b/254490217): Move this check into SafeActivityOptions
+ if (controller.mAtmInternal.isCallerRecents(Binder.getCallingUid())) {
+ finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
+ }
}
// Extract options before clearing calling identity
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index bf0052d..dbba9ee 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -19,16 +19,16 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.hdmi.HdmiPortInfo;
-import android.hardware.tv.cec.CecMessage;
-import android.hardware.tv.cec.IHdmiCec;
-import android.hardware.tv.cec.IHdmiCecCallback;
import android.hardware.tv.cec.V1_0.HotplugEvent;
import android.hardware.tv.cec.V1_0.IHdmiCec.getPhysicalAddressCallback;
import android.hardware.tv.cec.V1_0.OptionKey;
import android.hardware.tv.cec.V1_0.Result;
import android.hardware.tv.cec.V1_0.SendMessageResult;
-import android.hardware.tv.hdmi.IHdmi;
-import android.hardware.tv.hdmi.IHdmiCallback;
+import android.hardware.tv.hdmi.cec.CecMessage;
+import android.hardware.tv.hdmi.cec.IHdmiCec;
+import android.hardware.tv.hdmi.cec.IHdmiCecCallback;
+import android.hardware.tv.hdmi.connection.IHdmiConnection;
+import android.hardware.tv.hdmi.connection.IHdmiConnectionCallback;
import android.icu.util.IllformedLocaleException;
import android.icu.util.ULocale;
import android.os.Binder;
@@ -178,7 +178,7 @@
if (controller != null) {
return controller;
}
- HdmiLogger.warning("Unable to use CEC and HDMI AIDL HALs");
+ HdmiLogger.warning("Unable to use CEC and HDMI Connection AIDL HALs");
controller = createWithNativeWrapper(service, new NativeWrapperImpl11(), atomWriter);
if (controller != null) {
@@ -872,14 +872,14 @@
private static final class NativeWrapperImplAidl
implements NativeWrapper, IBinder.DeathRecipient {
private IHdmiCec mHdmiCec;
- private IHdmi mHdmi;
+ private IHdmiConnection mHdmiConnection;
@Nullable private HdmiCecCallback mCallback;
private final Object mLock = new Object();
@Override
public String nativeInit() {
- return connectToHal() ? mHdmiCec.toString() + " " + mHdmi.toString() : null;
+ return connectToHal() ? mHdmiCec.toString() + " " + mHdmiConnection.toString() : null;
}
boolean connectToHal() {
@@ -896,15 +896,15 @@
HdmiLogger.error("Couldn't link to death : ", e);
}
- mHdmi =
- IHdmi.Stub.asInterface(
- ServiceManager.getService(IHdmi.DESCRIPTOR + "/default"));
- if (mHdmi == null) {
- HdmiLogger.error("Could not initialize HDMI AIDL HAL");
+ mHdmiConnection =
+ IHdmiConnection.Stub.asInterface(
+ ServiceManager.getService(IHdmiConnection.DESCRIPTOR + "/default"));
+ if (mHdmiConnection == null) {
+ HdmiLogger.error("Could not initialize HDMI Connection AIDL HAL");
return false;
}
try {
- mHdmi.asBinder().linkToDeath(this, 0);
+ mHdmiConnection.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
HdmiLogger.error("Couldn't link to death : ", e);
}
@@ -915,8 +915,8 @@
public void binderDied() {
// One of the services died, try to reconnect to both.
mHdmiCec.asBinder().unlinkToDeath(this, 0);
- mHdmi.asBinder().unlinkToDeath(this, 0);
- HdmiLogger.error("HDMI or CEC service died, reconnecting");
+ mHdmiConnection.asBinder().unlinkToDeath(this, 0);
+ HdmiLogger.error("HDMI Connection or CEC service died, reconnecting");
connectToHal();
// Reconnect the callback
if (mCallback != null) {
@@ -935,7 +935,7 @@
}
try {
// Create an AIDL callback that can callback onHotplugEvent
- mHdmi.setCallback(new HdmiCallbackAidl(callback));
+ mHdmiConnection.setCallback(new HdmiConnectionCallbackAidl(callback));
} catch (RemoteException e) {
HdmiLogger.error("Couldn't initialise tv.hdmi callback : ", e);
}
@@ -1052,10 +1052,11 @@
@Override
public HdmiPortInfo[] nativeGetPortInfos() {
try {
- android.hardware.tv.hdmi.HdmiPortInfo[] hdmiPortInfos = mHdmi.getPortInfo();
+ android.hardware.tv.hdmi.connection.HdmiPortInfo[] hdmiPortInfos =
+ mHdmiConnection.getPortInfo();
HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[hdmiPortInfos.length];
int i = 0;
- for (android.hardware.tv.hdmi.HdmiPortInfo portInfo : hdmiPortInfos) {
+ for (android.hardware.tv.hdmi.connection.HdmiPortInfo portInfo : hdmiPortInfos) {
hdmiPortInfo[i] =
new HdmiPortInfo(
portInfo.portId,
@@ -1076,7 +1077,7 @@
@Override
public boolean nativeIsConnected(int port) {
try {
- return mHdmi.isConnected(port);
+ return mHdmiConnection.isConnected(port);
} catch (RemoteException e) {
HdmiLogger.error("Failed to get connection info : ", e);
return false;
@@ -1580,10 +1581,10 @@
}
}
- private static final class HdmiCallbackAidl extends IHdmiCallback.Stub {
+ private static final class HdmiConnectionCallbackAidl extends IHdmiConnectionCallback.Stub {
private final HdmiCecCallback mHdmiCecCallback;
- HdmiCallbackAidl(HdmiCecCallback hdmiCecCallback) {
+ HdmiConnectionCallbackAidl(HdmiCecCallback hdmiCecCallback) {
mHdmiCecCallback = hdmiCecCallback;
}
@@ -1594,12 +1595,12 @@
@Override
public synchronized String getInterfaceHash() throws android.os.RemoteException {
- return IHdmiCallback.Stub.HASH;
+ return IHdmiConnectionCallback.Stub.HASH;
}
@Override
public int getInterfaceVersion() throws android.os.RemoteException {
- return IHdmiCallback.Stub.VERSION;
+ return IHdmiConnectionCallback.Stub.VERSION;
}
}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 4d525da..9b42cfc 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.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 @@
/**
* 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 @@
*/
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 @@
ERROR_PROVIDER_MISMATCH,
ERROR_KEYSTORE_FAILURE,
ERROR_NO_NETWORK,
+ ERROR_TIMEOUT_EXHAUSTED,
})
@Retention(RetentionPolicy.SOURCE)
@interface RebootEscrowErrorCode {
@@ -147,6 +152,7 @@
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 @@
/** 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 @@
PowerManager.WakeLock mWakeLock;
+ private ConnectivityManager.NetworkCallback mNetworkCallback;
interface Callbacks {
boolean isUserSecure(int userId);
@@ -246,6 +262,11 @@
"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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
? 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 @@
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 @@
.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 @@
}
int vbmetaDigestStatus = getVbmetaDigestStatusOnRestoreComplete();
- if (!success && mLoadEscrowDataErrorCode == ERROR_NONE) {
- mLoadEscrowDataErrorCode = ERROR_UNKNOWN;
+ if (!success) {
+ compareAndSetLoadEscrowDataErrorCode(ERROR_NONE, ERROR_UNKNOWN, retryHandler);
}
- Slog.i(TAG, "Reporting RoR recovery metrics, success: " + success + ", service type: "
- + serviceType + ", error code: " + mLoadEscrowDataErrorCode);
+ 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);
+ mInjector.reportMetric(
+ success,
+ mLoadEscrowDataErrorCode,
+ serviceType,
+ attemptCount,
+ escrowDurationInSeconds,
+ vbmetaDigestStatus,
+ -1);
- mLoadEscrowDataErrorCode = ERROR_NONE;
+ 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 @@
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 7468d32..b6bdd4c 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1977,34 +1977,39 @@
return (haystack & needle) != 0;
}
- public boolean isInLockDownMode() {
- return mIsInLockDownMode;
+ // Return whether the user is in lockdown mode.
+ // If the flag is not set, we assume the user is not in lockdown.
+ public boolean isInLockDownMode(int userId) {
+ return mUserInLockDownMode.get(userId, false);
}
@Override
public synchronized void onStrongAuthRequiredChanged(int userId) {
boolean userInLockDownModeNext = containsFlag(getStrongAuthForUser(userId),
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
- mUserInLockDownMode.put(userId, userInLockDownModeNext);
- boolean isInLockDownModeNext = mUserInLockDownMode.indexOfValue(true) != -1;
- if (mIsInLockDownMode == isInLockDownModeNext) {
+ // Nothing happens if the lockdown mode of userId keeps the same.
+ if (userInLockDownModeNext == isInLockDownMode(userId)) {
return;
}
- if (isInLockDownModeNext) {
- cancelNotificationsWhenEnterLockDownMode();
+ // When the lockdown mode is changed, we perform the following steps.
+ // If the userInLockDownModeNext is true, all the function calls to
+ // notifyPostedLocked and notifyRemovedLocked will not be executed.
+ // The cancelNotificationsWhenEnterLockDownMode calls notifyRemovedLocked
+ // and postNotificationsWhenExitLockDownMode calls notifyPostedLocked.
+ // So we shall call cancelNotificationsWhenEnterLockDownMode before
+ // we set mUserInLockDownMode as true.
+ // On the other hand, if the userInLockDownModeNext is false, we shall call
+ // postNotificationsWhenExitLockDownMode after we put false into mUserInLockDownMode
+ if (userInLockDownModeNext) {
+ cancelNotificationsWhenEnterLockDownMode(userId);
}
- // When the mIsInLockDownMode is true, both notifyPostedLocked and
- // notifyRemovedLocked will be dismissed. So we shall call
- // cancelNotificationsWhenEnterLockDownMode before we set mIsInLockDownMode
- // as true and call postNotificationsWhenExitLockDownMode after we set
- // mIsInLockDownMode as false.
- mIsInLockDownMode = isInLockDownModeNext;
+ mUserInLockDownMode.put(userId, userInLockDownModeNext);
- if (!isInLockDownModeNext) {
- postNotificationsWhenExitLockDownMode();
+ if (!userInLockDownModeNext) {
+ postNotificationsWhenExitLockDownMode(userId);
}
}
}
@@ -4953,16 +4958,7 @@
}
enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
- // If the caller is system, take the package name from the rule's owner rather than
- // from the caller's package.
- String rulePkg = pkg;
- if (isCallingUidSystem()) {
- if (automaticZenRule.getOwner() != null) {
- rulePkg = automaticZenRule.getOwner().getPackageName();
- }
- }
-
- return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule,
+ return mZenModeHelper.addAutomaticZenRule(pkg, automaticZenRule,
"addAutomaticZenRule");
}
@@ -9713,11 +9709,14 @@
}
}
- private void cancelNotificationsWhenEnterLockDownMode() {
+ private void cancelNotificationsWhenEnterLockDownMode(int userId) {
synchronized (mNotificationLock) {
int numNotifications = mNotificationList.size();
for (int i = 0; i < numNotifications; i++) {
NotificationRecord rec = mNotificationList.get(i);
+ if (rec.getUser().getIdentifier() != userId) {
+ continue;
+ }
mListeners.notifyRemovedLocked(rec, REASON_CANCEL_ALL,
rec.getStats());
}
@@ -9725,14 +9724,23 @@
}
}
- private void postNotificationsWhenExitLockDownMode() {
+ private void postNotificationsWhenExitLockDownMode(int userId) {
synchronized (mNotificationLock) {
int numNotifications = mNotificationList.size();
+ // Set the delay to spread out the burst of notifications.
+ long delay = 0;
for (int i = 0; i < numNotifications; i++) {
NotificationRecord rec = mNotificationList.get(i);
- mListeners.notifyPostedLocked(rec, rec);
+ if (rec.getUser().getIdentifier() != userId) {
+ continue;
+ }
+ mHandler.postDelayed(() -> {
+ synchronized (mNotificationLock) {
+ mListeners.notifyPostedLocked(rec, rec);
+ }
+ }, delay);
+ delay += 20;
}
-
}
}
@@ -9911,6 +9919,9 @@
for (int i = 0; i < N; i++) {
NotificationRecord record = mNotificationList.get(i);
+ if (isInLockDownMode(record.getUser().getIdentifier())) {
+ continue;
+ }
if (!isVisibleToListener(record.getSbn(), record.getNotificationType(), info)) {
continue;
}
@@ -9952,8 +9963,8 @@
rankings.toArray(new NotificationListenerService.Ranking[0]));
}
- boolean isInLockDownMode() {
- return mStrongAuthTracker.isInLockDownMode();
+ boolean isInLockDownMode(int userId) {
+ return mStrongAuthTracker.isInLockDownMode(userId);
}
boolean hasCompanionDevice(ManagedServiceInfo info) {
@@ -11015,7 +11026,7 @@
@GuardedBy("mNotificationLock")
void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
boolean notifyAllListeners) {
- if (isInLockDownMode()) {
+ if (isInLockDownMode(r.getUser().getIdentifier())) {
return;
}
@@ -11121,7 +11132,7 @@
@GuardedBy("mNotificationLock")
public void notifyRemovedLocked(NotificationRecord r, int reason,
NotificationStats notificationStats) {
- if (isInLockDownMode()) {
+ if (isInLockDownMode(r.getUser().getIdentifier())) {
return;
}
@@ -11170,10 +11181,6 @@
*/
@GuardedBy("mNotificationLock")
public void notifyRankingUpdateLocked(List<NotificationRecord> changedHiddenNotifications) {
- if (isInLockDownMode()) {
- return;
- }
-
boolean isHiddenRankingUpdate = changedHiddenNotifications != null
&& changedHiddenNotifications.size() > 0;
// TODO (b/73052211): if the ranking update changed the notification type,
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 4c23ab8..d426679 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -326,7 +326,7 @@
public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule,
String reason) {
- if (!ZenModeConfig.SYSTEM_AUTHORITY.equals(pkg)) {
+ if (!isSystemRule(automaticZenRule)) {
PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner());
if (component == null) {
component = getActivityInfo(automaticZenRule.getConfigurationActivity());
@@ -582,6 +582,11 @@
}
}
+ private boolean isSystemRule(AutomaticZenRule rule) {
+ return rule.getOwner() != null
+ && ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName());
+ }
+
private ServiceInfo getServiceInfo(ComponentName owner) {
Intent queryIntent = new Intent();
queryIntent.setComponent(owner);
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index 1407530..e056151 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 @@
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 014d580..554e269 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -644,8 +644,8 @@
Permission bp = mRegistry.getPermission(info.name);
added = bp == null;
int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
+ enforcePermissionCapLocked(info, tree);
if (added) {
- enforcePermissionCapLocked(info, tree);
bp = new Permission(info.name, tree.getPackageName(), Permission.TYPE_DYNAMIC);
} else if (!bp.isDynamic()) {
throw new SecurityException("Not allowed to modify non-dynamic permission "
@@ -2136,6 +2136,46 @@
}
/**
+ * If the package was below api 23, got the SYSTEM_ALERT_WINDOW permission automatically, and
+ * then updated past api 23, and the app does not satisfy any of the other SAW permission flags,
+ * the permission should be revoked.
+ *
+ * @param newPackage The new package that was installed
+ * @param oldPackage The old package that was updated
+ */
+ private void revokeSystemAlertWindowIfUpgradedPast23(
+ @NonNull AndroidPackage newPackage,
+ @NonNull AndroidPackage oldPackage) {
+ if (oldPackage.getTargetSdkVersion() >= Build.VERSION_CODES.M
+ || newPackage.getTargetSdkVersion() < Build.VERSION_CODES.M
+ || !newPackage.getRequestedPermissions()
+ .contains(Manifest.permission.SYSTEM_ALERT_WINDOW)) {
+ return;
+ }
+
+ Permission saw;
+ synchronized (mLock) {
+ saw = mRegistry.getPermission(Manifest.permission.SYSTEM_ALERT_WINDOW);
+ }
+ final PackageStateInternal ps =
+ mPackageManagerInt.getPackageStateInternal(newPackage.getPackageName());
+ if (shouldGrantPermissionByProtectionFlags(newPackage, ps, saw, new ArraySet<>())
+ || shouldGrantPermissionBySignature(newPackage, saw)) {
+ return;
+ }
+ for (int userId : getAllUserIds()) {
+ try {
+ revokePermissionFromPackageForUser(newPackage.getPackageName(),
+ Manifest.permission.SYSTEM_ALERT_WINDOW, false, userId,
+ mDefaultPermissionCallback);
+ } catch (IllegalStateException | SecurityException e) {
+ Log.e(TAG, "unable to revoke SYSTEM_ALERT_WINDOW for "
+ + newPackage.getPackageName() + " user " + userId, e);
+ }
+ }
+ }
+
+ /**
* We might auto-grant permissions if any permission of the group is already granted. Hence if
* the group of a granted permission changes we need to revoke it to avoid having permissions of
* the new group auto-granted.
@@ -4661,6 +4701,7 @@
if (hasOldPkg) {
revokeRuntimePermissionsIfGroupChangedInternal(pkg, oldPkg);
revokeStoragePermissionsIfScopeExpandedInternal(pkg, oldPkg);
+ revokeSystemAlertWindowIfUpgradedPast23(pkg, oldPkg);
}
if (hasPermissionDefinitionChanges) {
revokeRuntimePermissionsIfPermissionDefinitionChangedInternal(
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index ed4ba0d..7f8f406 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 @@
}
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 0000000..e983968
--- /dev/null
+++ b/services/core/java/com/android/server/security/rkp/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "presubmit": [
+ {
+ "name": "RemoteProvisioningServiceTests"
+ }
+ ]
+}
+
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index fd6c974..b160af6a 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -98,7 +98,7 @@
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
return mService.getRecentTasks().createRecentTaskInfo(task,
- false /* stripExtras */);
+ false /* stripExtras */, true /* getTasksAllowed */);
} finally {
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 4860762..1fc061b 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -976,7 +976,7 @@
continue;
}
- res.add(createRecentTaskInfo(task, true /* stripExtras */));
+ res.add(createRecentTaskInfo(task, true /* stripExtras */, getTasksAllowed));
}
return res;
}
@@ -1895,7 +1895,8 @@
/**
* Creates a new RecentTaskInfo from a Task.
*/
- ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras) {
+ ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras,
+ boolean getTasksAllowed) {
final ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
// If the recent Task is detached, we consider it will be re-attached to the default
// TaskDisplayArea because we currently only support recent overview in the default TDA.
@@ -1907,6 +1908,9 @@
rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID;
rti.persistentId = rti.taskId;
rti.lastSnapshotData.set(tr.mLastTaskSnapshotData);
+ if (!getTasksAllowed) {
+ Task.trimIneffectiveInfo(tr, rti);
+ }
// Fill in organized child task info for the task created by organizer.
if (tr.mCreatedByOrganizer) {
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 33f019e..4e339f1 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -177,6 +177,10 @@
}
// Fill in some deprecated values
rti.id = rti.taskId;
+
+ if (!mAllowed) {
+ Task.trimIneffectiveInfo(task, rti);
+ }
return rti;
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d6f295e..8993840 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3447,6 +3447,27 @@
info.isSleeping = shouldSleepActivities();
}
+ /**
+ * Removes the activity info if the activity belongs to a different uid, which is
+ * different from the app that hosts the task.
+ */
+ static void trimIneffectiveInfo(Task task, TaskInfo info) {
+ final ActivityRecord baseActivity = task.getActivity(r -> !r.finishing,
+ false /* traverseTopToBottom */);
+ final int baseActivityUid =
+ baseActivity != null ? baseActivity.getUid() : task.effectiveUid;
+
+ if (info.topActivityInfo != null
+ && task.effectiveUid != info.topActivityInfo.applicationInfo.uid) {
+ info.topActivity = null;
+ info.topActivityInfo = null;
+ }
+
+ if (task.effectiveUid != baseActivityUid) {
+ info.baseActivity = null;
+ }
+ }
+
@Nullable PictureInPictureParams getPictureInPictureParams() {
final Task topTask = getTopMostTask();
if (topTask == null) return null;
diff --git a/services/tests/RemoteProvisioningServiceTests/Android.bp b/services/tests/RemoteProvisioningServiceTests/Android.bp
new file mode 100644
index 0000000..075680a
--- /dev/null
+++ b/services/tests/RemoteProvisioningServiceTests/Android.bp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "RemoteProvisioningServiceTests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "android.security.rkp_aidl-java",
+ "androidx.test.core",
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "mockito-target",
+ "service-rkp.impl",
+ "services.core",
+ "truth-prebuilt",
+ ],
+ test_suites: [
+ "device-tests",
+ ],
+ platform_apis: true,
+}
diff --git a/services/tests/RemoteProvisioningServiceTests/AndroidManifest.xml b/services/tests/RemoteProvisioningServiceTests/AndroidManifest.xml
new file mode 100644
index 0000000..7c12e189
--- /dev/null
+++ b/services/tests/RemoteProvisioningServiceTests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.security.rkp.test">
+
+ <application android:testOnly="true">
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.security.rkp.test"
+ android:label="Remote Provisioning System Service Tests"/>
+</manifest>
diff --git a/services/tests/RemoteProvisioningServiceTests/AndroidTest.xml b/services/tests/RemoteProvisioningServiceTests/AndroidTest.xml
new file mode 100644
index 0000000..bf86fc8
--- /dev/null
+++ b/services/tests/RemoteProvisioningServiceTests/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Frameworks RemoteProvisioningService Tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+ <option name="test-tag" value="RemoteProvisioningServiceTests" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="RemoteProvisioningServiceTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.server.security.rkp.test" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
+ </test>
+</configuration>
diff --git a/services/tests/RemoteProvisioningServiceTests/OWNERS b/services/tests/RemoteProvisioningServiceTests/OWNERS
new file mode 100644
index 0000000..348f940
--- /dev/null
+++ b/services/tests/RemoteProvisioningServiceTests/OWNERS
@@ -0,0 +1 @@
+file:platform/frameworks/base:master:/core/java/android/security/rkp/OWNERS
diff --git a/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java
new file mode 100644
index 0000000..470f2be
--- /dev/null
+++ b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security.rkp;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.AdditionalAnswers.answerVoid;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
+import android.os.OutcomeReceiver;
+import android.security.rkp.IGetKeyCallback;
+import android.security.rkp.service.RegistrationProxy;
+import android.security.rkp.service.RemotelyProvisionedKey;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.stubbing.Answer;
+import org.mockito.stubbing.VoidAnswer4;
+
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.concurrent.Executor;
+
+/**
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:RemoteProvisioningRegistrationTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class RemoteProvisioningRegistrationTest {
+ private RegistrationProxy mRegistrationProxy;
+ private RemoteProvisioningRegistration mRegistration;
+
+ @Before
+ public void setUp() {
+ mRegistrationProxy = mock(RegistrationProxy.class);
+ mRegistration = new RemoteProvisioningRegistration(mRegistrationProxy, Runnable::run);
+ }
+
+ // answerVoid wrapper with explicit types, avoiding long signatures when mocking getKeyAsync.
+ static Answer<Void> answerGetKeyAsync(
+ VoidAnswer4<Integer, CancellationSignal, Executor,
+ OutcomeReceiver<RemotelyProvisionedKey, Exception>> answer) {
+ return answerVoid(answer);
+ }
+
+ // matcher helper, making it easier to match the different key types
+ private android.security.rkp.RemotelyProvisionedKey matches(
+ RemotelyProvisionedKey expectedKey) {
+ return argThat((android.security.rkp.RemotelyProvisionedKey key) ->
+ Arrays.equals(key.keyBlob, expectedKey.getKeyBlob())
+ && Arrays.equals(key.encodedCertChain, expectedKey.getEncodedCertChain())
+ );
+ }
+
+ @Test
+ public void getKeySuccess() throws Exception {
+ RemotelyProvisionedKey expectedKey = mock(RemotelyProvisionedKey.class);
+ doAnswer(
+ answerGetKeyAsync((keyId, cancelSignal, executor, receiver) ->
+ executor.execute(() -> receiver.onResult(expectedKey))))
+ .when(mRegistrationProxy).getKeyAsync(eq(42), any(), any(), any());
+
+ IGetKeyCallback callback = mock(IGetKeyCallback.class);
+ mRegistration.getKey(42, callback);
+ verify(callback).onSuccess(matches(expectedKey));
+ verifyNoMoreInteractions(callback);
+ }
+
+ @Test
+ public void getKeyHandlesError() throws Exception {
+ Exception expectedException = new Exception("oops!");
+ doAnswer(
+ answerGetKeyAsync((keyId, cancelSignal, executor, receiver) ->
+ executor.execute(() -> receiver.onError(expectedException))))
+ .when(mRegistrationProxy).getKeyAsync(eq(0), any(), any(), any());
+ IGetKeyCallback callback = mock(IGetKeyCallback.class);
+ mRegistration.getKey(0, callback);
+ verify(callback).onError(eq(expectedException.getMessage()));
+ verifyNoMoreInteractions(callback);
+ }
+
+ @Test
+ public void getKeyCancelDuringProxyOperation() throws Exception {
+ IGetKeyCallback callback = mock(IGetKeyCallback.class);
+ doAnswer(
+ answerGetKeyAsync((keyId, cancelSignal, executor, receiver) -> {
+ mRegistration.cancelGetKey(callback);
+ assertThat(cancelSignal.isCanceled()).isTrue();
+ executor.execute(() -> receiver.onError(new OperationCanceledException()));
+ }))
+ .when(mRegistrationProxy).getKeyAsync(eq(Integer.MAX_VALUE), any(), any(), any());
+
+ mRegistration.getKey(Integer.MAX_VALUE, callback);
+ verify(callback).onCancel();
+ verifyNoMoreInteractions(callback);
+ }
+
+ @Test
+ public void cancelGetKeyWithInvalidCallback() throws Exception {
+ IGetKeyCallback callback = mock(IGetKeyCallback.class);
+ assertThrows(IllegalArgumentException.class, () -> mRegistration.cancelGetKey(callback));
+ }
+
+ @Test
+ public void getKeyRejectsDuplicateCallback() throws Exception {
+ IGetKeyCallback callback = mock(IGetKeyCallback.class);
+ doAnswer(
+ answerGetKeyAsync((keyId, cancelSignal, executor, receiver) -> {
+ assertThrows(IllegalArgumentException.class, () ->
+ mRegistration.getKey(0, callback));
+ executor.execute(() -> receiver.onResult(mock(RemotelyProvisionedKey.class)));
+ }))
+ .when(mRegistrationProxy).getKeyAsync(anyInt(), any(), any(), any());
+
+ mRegistration.getKey(0, callback);
+ verify(callback, times(1)).onSuccess(any());
+ verifyNoMoreInteractions(callback);
+ }
+
+ @Test
+ public void getKeyCancelAfterCompleteFails() throws Exception {
+ IGetKeyCallback callback = mock(IGetKeyCallback.class);
+ doAnswer(
+ answerGetKeyAsync((keyId, cancelSignal, executor, receiver) ->
+ executor.execute(() ->
+ receiver.onResult(mock(RemotelyProvisionedKey.class))
+ )))
+ .when(mRegistrationProxy).getKeyAsync(eq(Integer.MIN_VALUE), any(), any(), any());
+
+ mRegistration.getKey(Integer.MIN_VALUE, callback);
+ verify(callback).onSuccess(any());
+ assertThrows(IllegalArgumentException.class, () -> mRegistration.cancelGetKey(callback));
+ verifyNoMoreInteractions(callback);
+ }
+
+ @Test
+ public void getKeyCatchesExceptionFromProxy() throws Exception {
+ Exception expectedException = new RuntimeException("oops! bad input!");
+ doThrow(expectedException)
+ .when(mRegistrationProxy)
+ .getKeyAsync(anyInt(), any(), any(), any());
+
+ IGetKeyCallback callback = mock(IGetKeyCallback.class);
+ mRegistration.getKey(0, callback);
+ verify(callback).onError(eq(expectedException.getMessage()));
+ assertThrows(IllegalArgumentException.class, () -> mRegistration.cancelGetKey(callback));
+ verifyNoMoreInteractions(callback);
+ }
+
+ @Test
+ public void storeUpgradedKeySuccess() throws Exception {
+ // TODO(b/262748535)
+ }
+
+ @Test
+ public void storeUpgradedKeyFails() throws Exception {
+ // TODO(b/262748535)
+ }
+
+ @Test
+ public void storeUpgradedCatchesExceptionFromProxy() throws Exception {
+ // TODO(b/262748535)
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index b01c1c8..ce6bd6c 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.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 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 @@
private RebootEscrowManager mService;
private SecretKey mAesKey;
private MockInjector mMockInjector;
+ private Handler mHandler;
public interface MockableRebootEscrowInjected {
int getBootCount();
@@ -132,6 +136,9 @@
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 @@
mRebootEscrow = rebootEscrow;
mServiceConnection = null;
mServerBased = false;
+ mWaitForInternet = false;
RebootEscrowProviderHalImpl.Injector halInjector =
new RebootEscrowProviderHalImpl.Injector() {
@Override
@@ -164,6 +172,7 @@
mServiceConnection = serviceConnection;
mRebootEscrow = null;
mServerBased = true;
+ mWaitForInternet = false;
RebootEscrowProviderServerBasedImpl.Injector injector =
new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection) {
@Override
@@ -199,11 +208,33 @@
}
@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 @@
}
@Override
+ public int getLoadEscrowTimeoutMillis() {
+ // Timeout in 3 seconds.
+ return 3000;
+ }
+
+ @Override
public String getVbmetaDigest(boolean other) {
return other ? "" : "fake digest";
}
@@ -291,6 +328,9 @@
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 @@
@Test
public void loadRebootEscrowDataIfAvailable_NothingAvailable_Success() throws Exception {
- mService.loadRebootEscrowDataIfAvailable(null);
+ mService.loadRebootEscrowDataIfAvailable(mHandler);
}
@Test
@@ -499,7 +539,7 @@
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 @@
// 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 @@
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 @@
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 @@
// 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 @@
}
@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 @@
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 @@
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 @@
// 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 @@
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 c5131c8..57e403c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -435,63 +435,112 @@
@Test
public void testNotifyPostedLockedInLockdownMode() {
- NotificationRecord r = mock(NotificationRecord.class);
- NotificationRecord old = mock(NotificationRecord.class);
+ NotificationRecord r0 = mock(NotificationRecord.class);
+ NotificationRecord old0 = mock(NotificationRecord.class);
+ UserHandle uh0 = mock(UserHandle.class);
- // before the lockdown mode
- when(mNm.isInLockDownMode()).thenReturn(false);
- mListeners.notifyPostedLocked(r, old, true);
- mListeners.notifyPostedLocked(r, old, false);
- verify(r, atLeast(2)).getSbn();
+ NotificationRecord r1 = mock(NotificationRecord.class);
+ NotificationRecord old1 = mock(NotificationRecord.class);
+ UserHandle uh1 = mock(UserHandle.class);
- // in the lockdown mode
- reset(r);
- reset(old);
- when(mNm.isInLockDownMode()).thenReturn(true);
- mListeners.notifyPostedLocked(r, old, true);
- mListeners.notifyPostedLocked(r, old, false);
- verify(r, never()).getSbn();
- }
+ // Neither user0 and user1 is in the lockdown mode
+ when(r0.getUser()).thenReturn(uh0);
+ when(uh0.getIdentifier()).thenReturn(0);
+ when(mNm.isInLockDownMode(0)).thenReturn(false);
- @Test
- public void testnotifyRankingUpdateLockedInLockdownMode() {
- List chn = mock(List.class);
+ when(r1.getUser()).thenReturn(uh1);
+ when(uh1.getIdentifier()).thenReturn(1);
+ when(mNm.isInLockDownMode(1)).thenReturn(false);
- // before the lockdown mode
- when(mNm.isInLockDownMode()).thenReturn(false);
- mListeners.notifyRankingUpdateLocked(chn);
- verify(chn, atLeast(1)).size();
+ mListeners.notifyPostedLocked(r0, old0, true);
+ mListeners.notifyPostedLocked(r0, old0, false);
+ verify(r0, atLeast(2)).getSbn();
- // in the lockdown mode
- reset(chn);
- when(mNm.isInLockDownMode()).thenReturn(true);
- mListeners.notifyRankingUpdateLocked(chn);
- verify(chn, never()).size();
+ mListeners.notifyPostedLocked(r1, old1, true);
+ mListeners.notifyPostedLocked(r1, old1, false);
+ verify(r1, atLeast(2)).getSbn();
+
+ // Reset
+ reset(r0);
+ reset(old0);
+ reset(r1);
+ reset(old1);
+
+ // Only user 0 is in the lockdown mode
+ when(r0.getUser()).thenReturn(uh0);
+ when(uh0.getIdentifier()).thenReturn(0);
+ when(mNm.isInLockDownMode(0)).thenReturn(true);
+
+ when(r1.getUser()).thenReturn(uh1);
+ when(uh1.getIdentifier()).thenReturn(1);
+ when(mNm.isInLockDownMode(1)).thenReturn(false);
+
+ mListeners.notifyPostedLocked(r0, old0, true);
+ mListeners.notifyPostedLocked(r0, old0, false);
+ verify(r0, never()).getSbn();
+
+ mListeners.notifyPostedLocked(r1, old1, true);
+ mListeners.notifyPostedLocked(r1, old1, false);
+ verify(r1, atLeast(2)).getSbn();
}
@Test
public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException {
- NotificationRecord r = mock(NotificationRecord.class);
- NotificationStats rs = mock(NotificationStats.class);
+ NotificationRecord r0 = mock(NotificationRecord.class);
+ NotificationStats rs0 = mock(NotificationStats.class);
+ UserHandle uh0 = mock(UserHandle.class);
+
+ NotificationRecord r1 = mock(NotificationRecord.class);
+ NotificationStats rs1 = mock(NotificationStats.class);
+ UserHandle uh1 = mock(UserHandle.class);
+
StatusBarNotification sbn = mock(StatusBarNotification.class);
FieldSetter.setField(mNm,
NotificationManagerService.class.getDeclaredField("mHandler"),
mock(NotificationManagerService.WorkerHandler.class));
- // before the lockdown mode
- when(mNm.isInLockDownMode()).thenReturn(false);
- when(r.getSbn()).thenReturn(sbn);
- mListeners.notifyRemovedLocked(r, 0, rs);
- mListeners.notifyRemovedLocked(r, 0, rs);
- verify(r, atLeast(2)).getSbn();
+ // Neither user0 and user1 is in the lockdown mode
+ when(r0.getUser()).thenReturn(uh0);
+ when(uh0.getIdentifier()).thenReturn(0);
+ when(mNm.isInLockDownMode(0)).thenReturn(false);
+ when(r0.getSbn()).thenReturn(sbn);
- // in the lockdown mode
- reset(r);
- reset(rs);
- when(mNm.isInLockDownMode()).thenReturn(true);
- when(r.getSbn()).thenReturn(sbn);
- mListeners.notifyRemovedLocked(r, 0, rs);
- mListeners.notifyRemovedLocked(r, 0, rs);
- verify(r, never()).getSbn();
+ when(r1.getUser()).thenReturn(uh1);
+ when(uh1.getIdentifier()).thenReturn(1);
+ when(mNm.isInLockDownMode(1)).thenReturn(false);
+ when(r1.getSbn()).thenReturn(sbn);
+
+ mListeners.notifyRemovedLocked(r0, 0, rs0);
+ mListeners.notifyRemovedLocked(r0, 0, rs0);
+ verify(r0, atLeast(2)).getSbn();
+
+ mListeners.notifyRemovedLocked(r1, 0, rs1);
+ mListeners.notifyRemovedLocked(r1, 0, rs1);
+ verify(r1, atLeast(2)).getSbn();
+
+ // Reset
+ reset(r0);
+ reset(rs0);
+ reset(r1);
+ reset(rs1);
+
+ // Only user 0 is in the lockdown mode
+ when(r0.getUser()).thenReturn(uh0);
+ when(uh0.getIdentifier()).thenReturn(0);
+ when(mNm.isInLockDownMode(0)).thenReturn(true);
+ when(r0.getSbn()).thenReturn(sbn);
+
+ when(r1.getUser()).thenReturn(uh1);
+ when(uh1.getIdentifier()).thenReturn(1);
+ when(mNm.isInLockDownMode(1)).thenReturn(false);
+ when(r1.getSbn()).thenReturn(sbn);
+
+ mListeners.notifyRemovedLocked(r0, 0, rs0);
+ mListeners.notifyRemovedLocked(r0, 0, rs0);
+ verify(r0, never()).getSbn();
+
+ mListeners.notifyRemovedLocked(r1, 0, rs1);
+ mListeners.notifyRemovedLocked(r1, 0, rs1);
+ verify(r1, atLeast(2)).getSbn();
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 4d1c786..dd43a6b 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -174,6 +174,7 @@
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.NotificationListenerFilter;
import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationRankingUpdate;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenPolicy;
@@ -7604,43 +7605,6 @@
}
@Test
- public void testAddAutomaticZenRule_systemCallTakesPackageFromOwner() throws Exception {
- mService.isSystemUid = true;
- ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class);
- when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
- .thenReturn(true);
- mService.setZenHelper(mockZenModeHelper);
- ComponentName owner = new ComponentName("android", "ProviderName");
- ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build();
- boolean isEnabled = true;
- AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
- zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule, "com.android.settings");
-
- // verify that zen mode helper gets passed in a package name of "android"
- verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString());
- }
-
- @Test
- public void testAddAutomaticZenRule_nonSystemCallTakesPackageFromArg() throws Exception {
- mService.isSystemUid = false;
- ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class);
- when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
- .thenReturn(true);
- mService.setZenHelper(mockZenModeHelper);
- ComponentName owner = new ComponentName("android", "ProviderName");
- ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build();
- boolean isEnabled = true;
- AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
- zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule, "another.package");
-
- // verify that zen mode helper gets passed in the package name from the arg, not the owner
- verify(mockZenModeHelper).addAutomaticZenRule(
- eq("another.package"), eq(rule), anyString());
- }
-
- @Test
public void testAreNotificationsEnabledForPackage() throws Exception {
mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
mUid);
@@ -9837,10 +9801,10 @@
mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
- assertTrue(mStrongAuthTracker.isInLockDownMode());
- mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
+ assertTrue(mStrongAuthTracker.isInLockDownMode(mContext.getUserId()));
+ mStrongAuthTracker.setGetStrongAuthForUserReturnValue(mContext.getUserId());
mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
- assertFalse(mStrongAuthTracker.isInLockDownMode());
+ assertFalse(mStrongAuthTracker.isInLockDownMode(mContext.getUserId()));
}
@Test
@@ -9856,8 +9820,8 @@
// when entering the lockdown mode, cancel the 2 notifications.
mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
- mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
- assertTrue(mStrongAuthTracker.isInLockDownMode());
+ mStrongAuthTracker.onStrongAuthRequiredChanged(0);
+ assertTrue(mStrongAuthTracker.isInLockDownMode(0));
// the notifyRemovedLocked function is called twice due to REASON_CANCEL_ALL.
ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
@@ -9866,10 +9830,46 @@
// exit lockdown mode.
mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
- mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
+ mStrongAuthTracker.onStrongAuthRequiredChanged(0);
+ assertFalse(mStrongAuthTracker.isInLockDownMode(0));
// the notifyPostedLocked function is called twice.
- verify(mListeners, times(2)).notifyPostedLocked(any(), any());
+ verify(mWorkerHandler, times(2)).postDelayed(any(Runnable.class), anyLong());
+ //verify(mListeners, times(2)).notifyPostedLocked(any(), any());
+ }
+
+ @Test
+ public void testMakeRankingUpdateLockedInLockDownMode() {
+ // post 2 notifications from a same package
+ NotificationRecord pkgA = new NotificationRecord(mContext,
+ generateSbn("a", 1000, 9, 0), mTestNotificationChannel);
+ mService.addNotification(pkgA);
+ NotificationRecord pkgB = new NotificationRecord(mContext,
+ generateSbn("a", 1000, 9, 1), mTestNotificationChannel);
+ mService.addNotification(pkgB);
+
+ mService.setIsVisibleToListenerReturnValue(true);
+ NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(null);
+ assertEquals(2, nru.getRankingMap().getOrderedKeys().length);
+
+ // when only user 0 entering the lockdown mode, its notification will be suppressed.
+ mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
+ STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ mStrongAuthTracker.onStrongAuthRequiredChanged(0);
+ assertTrue(mStrongAuthTracker.isInLockDownMode(0));
+ assertFalse(mStrongAuthTracker.isInLockDownMode(1));
+
+ nru = mService.makeRankingUpdateLocked(null);
+ assertEquals(1, nru.getRankingMap().getOrderedKeys().length);
+
+ // User 0 exits lockdown mode. Its notification will be resumed.
+ mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
+ mStrongAuthTracker.onStrongAuthRequiredChanged(0);
+ assertFalse(mStrongAuthTracker.isInLockDownMode(0));
+ assertFalse(mStrongAuthTracker.isInLockDownMode(1));
+
+ nru = mService.makeRankingUpdateLocked(null);
+ assertEquals(2, nru.getRankingMap().getOrderedKeys().length);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
index b49e5cb..8cf74fb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
@@ -19,10 +19,12 @@
import android.companion.ICompanionDeviceManager;
import android.content.ComponentName;
import android.content.Context;
+import android.service.notification.StatusBarNotification;
import androidx.annotation.Nullable;
import com.android.internal.logging.InstanceIdSequence;
+import com.android.server.notification.ManagedServices.ManagedServiceInfo;
import java.util.HashSet;
import java.util.Set;
@@ -37,6 +39,9 @@
@Nullable
NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback;
+ @Nullable
+ Boolean mIsVisibleToListenerReturnValue = null;
+
TestableNotificationManagerService(Context context, NotificationRecordLogger logger,
InstanceIdSequence notificationInstanceIdSequence) {
super(context, logger, notificationInstanceIdSequence);
@@ -119,6 +124,19 @@
mShowReviewPermissionsNotification = setting;
}
+ protected void setIsVisibleToListenerReturnValue(boolean value) {
+ mIsVisibleToListenerReturnValue = value;
+ }
+
+ @Override
+ boolean isVisibleToListener(StatusBarNotification sbn, int notificationType,
+ ManagedServiceInfo listener) {
+ if (mIsVisibleToListenerReturnValue != null) {
+ return mIsVisibleToListenerReturnValue;
+ }
+ return super.isVisibleToListener(sbn, notificationType, listener);
+ }
+
public class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker {
private int mGetStrongAuthForUserReturnValue = 0;
StrongAuthTrackerFake(Context context) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 2ccdcaa..4550b56 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -1672,36 +1672,6 @@
}
@Test
- public void testAddAutomaticZenRule_claimedSystemOwner() {
- // Make sure anything that claims to have a "system" owner but not actually part of the
- // system package still gets limited on number of rules
- for (int i = 0; i < RULE_LIMIT_PER_PACKAGE; i++) {
- ScheduleInfo si = new ScheduleInfo();
- si.startHour = i;
- AutomaticZenRule zenRule = new AutomaticZenRule("name" + i,
- new ComponentName("android", "ScheduleConditionProvider" + i),
- null, // configuration activity
- ZenModeConfig.toScheduleConditionId(si),
- new ZenPolicy.Builder().build(),
- NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test");
- assertNotNull(id);
- }
- try {
- AutomaticZenRule zenRule = new AutomaticZenRule("name",
- new ComponentName("android", "ScheduleConditionProviderFinal"),
- null, // configuration activity
- ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
- new ZenPolicy.Builder().build(),
- NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test");
- fail("allowed too many rules to be created");
- } catch (IllegalArgumentException e) {
- // yay
- }
- }
-
- @Test
public void testAddAutomaticZenRule_CA() {
AutomaticZenRule zenRule = new AutomaticZenRule("name",
null,
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index adf694c..0462e1b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -30,6 +30,7 @@
import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.os.Process.NOBODY_UID;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -1220,20 +1221,34 @@
@Test
public void testCreateRecentTaskInfo_detachedTask() {
- final Task task = createTaskBuilder(".Task").setCreateActivity(true).build();
+ final Task task = createTaskBuilder(".Task").build();
+ new ActivityBuilder(mSupervisor.mService)
+ .setTask(task)
+ .setUid(NOBODY_UID)
+ .setComponent(getUniqueComponentName())
+ .build();
final TaskDisplayArea tda = task.getDisplayArea();
assertTrue(task.isAttached());
assertTrue(task.supportsMultiWindow());
- RecentTaskInfo info = mRecentTasks.createRecentTaskInfo(task, true);
+ RecentTaskInfo info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+ true /* getTasksAllowed */);
assertTrue(info.supportsMultiWindow);
+ info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+ false /* getTasksAllowed */);
+
+ assertTrue(info.topActivity == null);
+ assertTrue(info.topActivityInfo == null);
+ assertTrue(info.baseActivity == null);
+
// The task can be put in split screen even if it is not attached now.
task.removeImmediately();
- info = mRecentTasks.createRecentTaskInfo(task, true);
+ info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+ true /* getTasksAllowed */);
assertTrue(info.supportsMultiWindow);
@@ -1242,7 +1257,8 @@
doReturn(false).when(tda).supportsNonResizableMultiWindow();
doReturn(false).when(task).isResizeable();
- info = mRecentTasks.createRecentTaskInfo(task, true);
+ info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+ true /* getTasksAllowed */);
assertFalse(info.supportsMultiWindow);
@@ -1250,7 +1266,8 @@
// the device supports it.
doReturn(true).when(tda).supportsNonResizableMultiWindow();
- info = mRecentTasks.createRecentTaskInfo(task, true);
+ info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+ true /* getTasksAllowed */);
assertTrue(info.supportsMultiWindow);
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 13550f0..f373f6e 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -478,6 +478,14 @@
public static final String SUBSCRIPTION_TYPE = SimInfo.COLUMN_SUBSCRIPTION_TYPE;
/**
+ * TelephonyProvider column name for last used TP - message Reference
+ * <P>Type: INTEGER (int)</P> with -1 as default value
+ * TP - Message Reference valid range [0 - 255]
+ * @hide
+ */
+ public static final String TP_MESSAGE_REF = SimInfo.COLUMN_TP_MESSAGE_REF;
+
+ /**
* TelephonyProvider column name data_enabled_override_rules.
* It's a list of rules for overriding data enabled settings. The syntax is
* For example, "mms=nonDefault" indicates enabling data for mms in non-default subscription.
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 3379ce5..b32f046 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1109,6 +1109,7 @@
sb.append(", ").append(mCarrierId);
sb.append(", ").append(mSkip464Xlat);
sb.append(", ").append(mAlwaysOn);
+ sb.append(", ").append(Objects.hash(mUser, mPassword));
return sb.toString();
}
@@ -1297,8 +1298,6 @@
other.mLingeringNetworkTypeBitmask)
&& Objects.equals(this.mProfileId, other.mProfileId)
&& Objects.equals(this.mPersistent, other.mPersistent)
- && Objects.equals(this.mMvnoType, other.mMvnoType)
- && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
&& Objects.equals(this.mApnSetId, other.mApnSetId)
&& Objects.equals(this.mCarrierId, other.mCarrierId)
&& Objects.equals(this.mSkip464Xlat, other.mSkip464Xlat)
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index b51e8d3d..7a5bf06 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -249,7 +249,6 @@
ENCODING_UNKNOWN, 0, 0);
}
-
/**
* Gets an SMS-SUBMIT PDU for a destination address and a message using the specified encoding.
*
@@ -272,7 +271,7 @@
boolean statusReportRequested, byte[] header, int encoding,
int languageTable, int languageShiftTable) {
return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested,
- header, encoding, languageTable, languageShiftTable, -1);
+ header, encoding, languageTable, languageShiftTable, -1, 0);
}
/**
@@ -297,6 +296,32 @@
String destinationAddress, String message,
boolean statusReportRequested, byte[] header, int encoding,
int languageTable, int languageShiftTable, int validityPeriod) {
+ return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, header,
+ encoding, languageTable, languageShiftTable, validityPeriod, 0);
+ }
+
+ /**
+ * Gets an SMS-SUBMIT PDU for a destination address and a message using the specified encoding.
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @param header a byte array containing the data for the User Data Header.
+ * @param encoding encoding defined by constants in
+ * com.android.internal.telephony.SmsConstants.ENCODING_*
+ * @param languageTable
+ * @param languageShiftTable
+ * @param validityPeriod Validity Period of the message in Minutes.
+ * @param messageRef TP Message Reference number
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
+ * @hide
+ */
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, String message,
+ boolean statusReportRequested, byte[] header, int encoding,
+ int languageTable, int languageShiftTable, int validityPeriod, int messageRef) {
// Perform null parameter checks.
if (message == null || destinationAddress == null) {
@@ -350,7 +375,7 @@
ByteArrayOutputStream bo = getSubmitPduHead(
scAddress, destinationAddress, mtiByte,
- statusReportRequested, ret);
+ statusReportRequested, ret, messageRef);
// Skip encoding pdu if error occurs when create pdu head and the error will be handled
// properly later on encodedMessage correctness check.
@@ -496,7 +521,7 @@
String destinationAddress, String message,
boolean statusReportRequested, int validityPeriod) {
return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested,
- null, ENCODING_UNKNOWN, 0, 0, validityPeriod);
+ null, ENCODING_UNKNOWN, 0, 0, validityPeriod, 0);
}
/**
@@ -507,12 +532,13 @@
* @param destinationPort the port to deliver the message to at the destination.
* @param data the data for the message.
* @param statusReportRequested indicates whether a report is reuested for this message.
+ * @param messageRef TP Message Reference number
* @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
* encoded message. Returns null on encode error.
*/
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, int destinationPort, byte[] data,
- boolean statusReportRequested) {
+ boolean statusReportRequested, int messageRef) {
SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs();
portAddrs.destPort = destinationPort;
@@ -533,7 +559,7 @@
SubmitPdu ret = new SubmitPdu();
ByteArrayOutputStream bo = getSubmitPduHead(
scAddress, destinationAddress, (byte) 0x41, /* TP-MTI=SMS-SUBMIT, TP-UDHI=true */
- statusReportRequested, ret);
+ statusReportRequested, ret, messageRef);
// Skip encoding pdu if error occurs when create pdu head and the error will be handled
// properly later on encodedMessage correctness check.
if (bo == null) return ret;
@@ -559,6 +585,24 @@
}
/**
+ * Gets an SMS-SUBMIT PDU for a data message to a destination address & port.
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param destinationPort the port to deliver the message to at the destination.
+ * @param data the data for the message.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
+ */
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, int destinationPort, byte[] data,
+ boolean statusReportRequested) {
+ return getSubmitPdu(scAddress, destinationAddress, destinationPort, data,
+ statusReportRequested, 0);
+ }
+
+ /**
* Creates the beginning of a SUBMIT PDU.
*
* This is the part of the SUBMIT PDU that is common to the two versions of
@@ -576,6 +620,28 @@
private static ByteArrayOutputStream getSubmitPduHead(
String scAddress, String destinationAddress, byte mtiByte,
boolean statusReportRequested, SubmitPdu ret) {
+ return getSubmitPduHead(scAddress, destinationAddress, mtiByte, statusReportRequested, ret,
+ 0);
+ }
+
+ /**
+ * Creates the beginning of a SUBMIT PDU.
+ *
+ * This is the part of the SUBMIT PDU that is common to the two versions of
+ * {@link #getSubmitPdu}, one of which takes a byte array and the other of which takes a
+ * <code>String</code>.
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param mtiByte
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @param ret <code>SubmitPdu</code>.
+ * @param messageRef TP Message Reference number
+ * @return a byte array of the beginning of a SUBMIT PDU. Null for invalid destinationAddress.
+ */
+ private static ByteArrayOutputStream getSubmitPduHead(
+ String scAddress, String destinationAddress, byte mtiByte,
+ boolean statusReportRequested, SubmitPdu ret, int messageRef) {
ByteArrayOutputStream bo = new ByteArrayOutputStream(
MAX_USER_DATA_BYTES + 40);
@@ -596,7 +662,7 @@
bo.write(mtiByte);
// space for TP-Message-Reference
- bo.write(0);
+ bo.write(messageRef);
byte[] daBytes;