Merge "Update the scrimColor and the shape of the modal bottom sheet"
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index e3bd5ac..dcc6aa6 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -29,6 +29,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
+import android.app.BroadcastOptions;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -314,7 +315,9 @@
private Sensor mMotionSensor;
private LocationRequest mLocationRequest;
private Intent mIdleIntent;
+ private Bundle mIdleIntentOptions;
private Intent mLightIdleIntent;
+ private Bundle mLightIdleIntentOptions;
private AnyMotionDetector mAnyMotionDetector;
private final AppStateTrackerImpl mAppStateTracker;
@GuardedBy("this")
@@ -1798,10 +1801,12 @@
} catch (RemoteException e) {
}
if (deepChanged) {
- getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
+ getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL,
+ null /* receiverPermission */, mIdleIntentOptions);
}
if (lightChanged) {
- getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL);
+ getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL,
+ null /* receiverPermission */, mLightIdleIntentOptions);
}
EventLogTags.writeDeviceIdleOnComplete();
mGoingIdleWakeLock.release();
@@ -1821,13 +1826,13 @@
incActiveIdleOps();
mLocalActivityManager.broadcastIntentWithCallback(mIdleIntent,
mIdleStartedDoneReceiver, null, UserHandle.USER_ALL,
- null, null, null);
+ null, null, mIdleIntentOptions);
}
if (lightChanged) {
incActiveIdleOps();
mLocalActivityManager.broadcastIntentWithCallback(mLightIdleIntent,
mIdleStartedDoneReceiver, null, UserHandle.USER_ALL,
- null, null, null);
+ null, null, mLightIdleIntentOptions);
}
// Always start with one active op for the message being sent here.
// Now we are done!
@@ -1849,10 +1854,12 @@
} catch (RemoteException e) {
}
if (deepChanged) {
- getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
+ getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL,
+ null /* receiverPermission */, mIdleIntentOptions);
}
if (lightChanged) {
- getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL);
+ getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL,
+ null /* receiverPermission */, mLightIdleIntentOptions);
}
EventLogTags.writeDeviceIdleOffComplete();
} break;
@@ -2531,6 +2538,9 @@
mLightIdleIntent = new Intent(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
mLightIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT);
+ mIdleIntentOptions = mLightIdleIntentOptions = options.toBundle();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index fa73178..725cef6 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1908,6 +1908,7 @@
}
public abstract class VibrationEffect implements android.os.Parcelable {
+ method @Nullable public abstract long[] computeCreateWaveformOffOnTimingsOrNull();
method public static android.os.VibrationEffect get(int);
method public static android.os.VibrationEffect get(int, boolean);
method @Nullable public static android.os.VibrationEffect get(android.net.Uri, android.content.Context);
@@ -1922,6 +1923,7 @@
}
public static final class VibrationEffect.Composed extends android.os.VibrationEffect {
+ method @Nullable public long[] computeCreateWaveformOffOnTimingsOrNull();
method public long getDuration();
method public int getRepeatIndex();
method @NonNull public java.util.List<android.os.vibrator.VibrationEffectSegment> getSegments();
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 71bc4b3..3448a9e 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -227,6 +227,31 @@
}
/**
+ * Computes a legacy vibration pattern (i.e. a pattern with duration values for "off/on"
+ * vibration components) that is equivalent to this VibrationEffect.
+ *
+ * <p>All non-repeating effects created with {@link #createWaveform(int[], int)} are convertible
+ * into an equivalent vibration pattern with this method. It is not guaranteed that an effect
+ * created with other means becomes converted into an equivalent legacy vibration pattern, even
+ * if it has an equivalent vibration pattern. If this method is unable to create an equivalent
+ * vibration pattern for such effects, it will return {@code null}.
+ *
+ * <p>Note that a valid equivalent long[] pattern cannot be created for an effect that has any
+ * form of repeating behavior, regardless of how the effect was created. For repeating effects,
+ * the method will always return {@code null}.
+ *
+ * @return a long array representing a vibration pattern equivalent to the VibrationEffect, if
+ * the method successfully derived a vibration pattern equivalent to the effect
+ * (this will always be the case if the effect was created via
+ * {@link #createWaveform(int[], int)} and is non-repeating). Otherwise, returns
+ * {@code null}.
+ * @hide
+ */
+ @TestApi
+ @Nullable
+ public abstract long[] computeCreateWaveformOffOnTimingsOrNull();
+
+ /**
* Create a waveform vibration.
*
* <p>Waveform vibrations are a potentially repeating series of timing and amplitude pairs,
@@ -641,6 +666,51 @@
return mRepeatIndex;
}
+ /** @hide */
+ @Override
+ @Nullable
+ public long[] computeCreateWaveformOffOnTimingsOrNull() {
+ if (getRepeatIndex() >= 0) {
+ // Repeating effects cannot be fully represented as a long[] legacy pattern.
+ return null;
+ }
+
+ List<VibrationEffectSegment> segments = getSegments();
+
+ // The maximum possible size of the final pattern is 1 plus the number of segments in
+ // the original effect. This is because we will add an empty "off" segment at the
+ // start of the pattern if the first segment of the original effect is an "on" segment.
+ // (because the legacy patterns start with an "off" pattern). Other than this one case,
+ // we will add the durations of back-to-back segments of similar amplitudes (amplitudes
+ // that are all "on" or "off") and create a pattern entry for the total duration, which
+ // will not take more number pattern entries than the number of segments processed.
+ long[] patternBuffer = new long[segments.size() + 1];
+ int patternIndex = 0;
+
+ for (int i = 0; i < segments.size(); i++) {
+ StepSegment stepSegment =
+ castToValidStepSegmentForOffOnTimingsOrNull(segments.get(i));
+ if (stepSegment == null) {
+ // This means that there is 1 or more segments of this effect that is/are not a
+ // possible component of a legacy vibration pattern. Thus, the VibrationEffect
+ // does not have any equivalent legacy vibration pattern.
+ return null;
+ }
+
+ boolean isSegmentOff = stepSegment.getAmplitude() == 0;
+ // Even pattern indices are "off", and odd pattern indices are "on"
+ boolean isCurrentPatternIndexOff = (patternIndex % 2) == 0;
+ if (isSegmentOff != isCurrentPatternIndexOff) {
+ // Move the pattern index one step ahead, so that the current segment's
+ // "off"/"on" property matches that of the index's
+ ++patternIndex;
+ }
+ patternBuffer[patternIndex] += stepSegment.getDuration();
+ }
+
+ return Arrays.copyOf(patternBuffer, patternIndex + 1);
+ }
+
/** @hide */
@Override
public void validate() {
@@ -806,6 +876,31 @@
return new Composed[size];
}
};
+
+ /**
+ * Casts a provided {@link VibrationEffectSegment} to a {@link StepSegment} and returns it,
+ * only if it can possibly be a segment for an effect created via
+ * {@link #createWaveform(int[], int)}. Otherwise, returns {@code null}.
+ */
+ @Nullable
+ private static StepSegment castToValidStepSegmentForOffOnTimingsOrNull(
+ VibrationEffectSegment segment) {
+ if (!(segment instanceof StepSegment)) {
+ return null;
+ }
+
+ StepSegment stepSegment = (StepSegment) segment;
+ if (stepSegment.getFrequencyHz() != 0) {
+ return null;
+ }
+
+ float amplitude = stepSegment.getAmplitude();
+ if (amplitude != 0 && amplitude != DEFAULT_AMPLITUDE) {
+ return null;
+ }
+
+ return stepSegment;
+ }
}
/**
diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/coretests/src/android/os/VibrationEffectTest.java
index f7ca822..0c7ff4a 100644
--- a/core/tests/coretests/src/android/os/VibrationEffectTest.java
+++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java
@@ -16,6 +16,7 @@
package android.os;
+import static android.os.VibrationEffect.DEFAULT_AMPLITUDE;
import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
import static android.os.VibrationEffect.VibrationParameter.targetFrequency;
@@ -48,6 +49,7 @@
import org.mockito.junit.MockitoJUnitRunner;
import java.time.Duration;
+import java.util.Arrays;
@Presubmit
@RunWith(MockitoJUnitRunner.class)
@@ -62,16 +64,363 @@
private static final int TEST_AMPLITUDE = 100;
private static final long[] TEST_TIMINGS = new long[] { 100, 100, 200 };
private static final int[] TEST_AMPLITUDES =
- new int[] { 255, 0, VibrationEffect.DEFAULT_AMPLITUDE };
+ new int[] { 255, 0, DEFAULT_AMPLITUDE };
private static final VibrationEffect TEST_ONE_SHOT =
VibrationEffect.createOneShot(TEST_TIMING, TEST_AMPLITUDE);
private static final VibrationEffect DEFAULT_ONE_SHOT =
- VibrationEffect.createOneShot(TEST_TIMING, VibrationEffect.DEFAULT_AMPLITUDE);
+ VibrationEffect.createOneShot(TEST_TIMING, DEFAULT_AMPLITUDE);
private static final VibrationEffect TEST_WAVEFORM =
VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, -1);
@Test
+ public void computeLegacyPattern_timingsAndAmplitudes_zeroAmplitudesOnEvenIndices() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3, 4, 5},
+ /* amplitudes= */ new int[] {0, DEFAULT_AMPLITUDE, 0, DEFAULT_AMPLITUDE, 0},
+ /* repeatIndex= */ -1);
+ long[] expectedPattern = new long[] {1, 2, 3, 4, 5};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_timingsAndAmplitudes_zeroAmplitudesOnOddIndices() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3, 4, 5},
+ /* amplitudes= */ new int[] {
+ DEFAULT_AMPLITUDE, 0, DEFAULT_AMPLITUDE, 0, DEFAULT_AMPLITUDE},
+ /* repeatIndex= */ -1);
+ long[] expectedPattern = new long[] {0, 1, 2, 3, 4, 5};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_timingsAndAmplitudes_zeroAmplitudesAtTheStart() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {0, 0, DEFAULT_AMPLITUDE},
+ /* repeatIndex= */ -1);
+ long[] expectedPattern = new long[] {3, 3};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_timingsAndAmplitudes_zeroAmplitudesAtTheEnd() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {DEFAULT_AMPLITUDE, 0, 0},
+ /* repeatIndex= */ -1);
+ long[] expectedPattern = new long[] {0, 1, 5};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_timingsAndAmplitudes_allDefaultAmplitudes() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {
+ DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE},
+ /* repeatIndex= */ -1);
+ long[] expectedPattern = new long[] {0, 6};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_timingsAndAmplitudes_allZeroAmplitudes() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {0, 0, 0},
+ /* repeatIndex= */ -1);
+ long[] expectedPattern = new long[] {6};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_timingsAndAmplitudes_sparsedZeroAmplitudes() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3, 4, 5, 6, 7},
+ /* amplitudes= */ new int[] {
+ 0, 0, DEFAULT_AMPLITUDE, 0, DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE, 0},
+ /* repeatIndex= */ -1);
+ long[] expectedPattern = new long[] {3, 3, 4, 11, 7};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_timingsAndAmplitudes_oneTimingWithDefaultAmplitude() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1},
+ /* amplitudes= */ new int[] {DEFAULT_AMPLITUDE},
+ /* repeatIndex= */ -1);
+ long[] expectedPattern = new long[] {0, 1};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_timingsAndAmplitudes_oneTimingWithZeroAmplitude() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1},
+ /* amplitudes= */ new int[] {0},
+ /* repeatIndex= */ -1);
+ long[] expectedPattern = new long[] {1};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_timingsAndAmplitudes_repeating() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3, 4, 5},
+ /* amplitudes= */ new int[] {0, DEFAULT_AMPLITUDE, 0, DEFAULT_AMPLITUDE, 0},
+ /* repeatIndex= */ 0);
+
+ assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
+
+ effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3, 4, 5},
+ /* amplitudes= */ new int[] {0, DEFAULT_AMPLITUDE, 0, DEFAULT_AMPLITUDE, 0},
+ /* repeatIndex= */ 3);
+
+ assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
+
+ effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2},
+ /* amplitudes= */ new int[] {DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE},
+ /* repeatIndex= */ 1);
+
+ assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_timingsAndAmplitudes_badAmplitude() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1},
+ /* amplitudes= */ new int[] {200},
+ /* repeatIndex= */ -1);
+
+ assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_timingsOnly_nonZeroTimings() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* repeatIndex= */ -1);
+ long[] expectedPattern = new long[] {1, 2, 3};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_timingsOnly_oneValue() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {5},
+ /* repeatIndex= */ -1);
+ long[] expectedPattern = new long[] {5};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_timingsOnly_zeroesAtTheEnd() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3, 0, 0},
+ /* repeatIndex= */ -1);
+ long[] expectedPattern = new long[] {1, 2, 3, 0, 0};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_timingsOnly_zeroesAtTheStart() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {0, 0, 1, 2, 3},
+ /* repeatIndex= */ -1);
+ long[] expectedPattern = new long[] {0, 0, 1, 2, 3};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_timingsOnly_zeroesAtTheMiddle() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 0, 0, 3, 4, 5},
+ /* repeatIndex= */ -1);
+ long[] expectedPattern = new long[] {1, 2, 0, 0, 3, 4, 5};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_timingsOnly_sparsedZeroes() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {0, 1, 2, 0, 0, 3, 4, 5, 0},
+ /* repeatIndex= */ -1);
+ long[] expectedPattern = new long[] {0, 1, 2, 0, 0, 3, 4, 5, 0};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_timingsOnly_repeating() {
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {0, 1, 2, 0, 0, 3, 4, 5, 0},
+ /* repeatIndex= */ 0);
+
+ assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
+
+ effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3, 4},
+ /* repeatIndex= */ 2);
+
+ assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_notPatternPased() {
+ VibrationEffect effect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK);
+
+ assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_oneShot_defaultAmplitude() {
+ VibrationEffect effect = VibrationEffect.createOneShot(
+ /* milliseconds= */ 5, /* ampliutde= */ DEFAULT_AMPLITUDE);
+ long[] expectedPattern = new long[] {0, 5};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_oneShot_badAmplitude() {
+ VibrationEffect effect = VibrationEffect.createOneShot(
+ /* milliseconds= */ 5, /* ampliutde= */ 50);
+
+ assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_composition_noOffDuration() {
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addEffect(
+ VibrationEffect.createWaveform(
+ /* timings= */ new long[] {5},
+ /* repeatIndex= */ -1))
+ .addEffect(
+ VibrationEffect.createWaveform(
+ /* timings= */ new long[] {2, 3},
+ /* repeatIndex= */ -1))
+ .addEffect(
+ VibrationEffect.createWaveform(
+ /* timings= */ new long[] {10, 20},
+ /* amplitudes= */ new int[] {DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE},
+ /* repeatIndex= */ -1))
+ .addEffect(
+ VibrationEffect.createWaveform(
+ /* timings= */ new long[] {4, 5},
+ /* amplitudes= */ new int[] {0, DEFAULT_AMPLITUDE},
+ /* repeatIndex= */ -1))
+ .compose();
+ long[] expectedPattern = new long[] {7, 33, 4, 5};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_composition_withOffDuration() {
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addOffDuration(Duration.ofMillis(20))
+ .addEffect(
+ VibrationEffect.createWaveform(
+ /* timings= */ new long[] {10, 20},
+ /* amplitudes= */ new int[] {0, DEFAULT_AMPLITUDE},
+ /* repeatIndex= */ -1))
+ .addEffect(
+ VibrationEffect.createWaveform(
+ /* timings= */ new long[] {30, 40},
+ /* amplitudes= */ new int[] {DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE},
+ /* repeatIndex= */ -1))
+ .addOffDuration(Duration.ofMillis(10))
+ .addEffect(
+ VibrationEffect.createWaveform(
+ /* timings= */ new long[] {4, 5},
+ /* repeatIndex= */ -1))
+ .addOffDuration(Duration.ofMillis(5))
+ .compose();
+ long[] expectedPattern = new long[] {30, 90, 14, 5, 5};
+
+ assertArrayEq(expectedPattern, effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_composition_withPrimitives() {
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ .addOffDuration(Duration.ofMillis(20))
+ .addEffect(
+ VibrationEffect.createWaveform(
+ /* timings= */ new long[] {5},
+ /* repeatIndex= */ -1))
+ .compose();
+
+ assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_composition_repeating() {
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addEffect(
+ VibrationEffect.createWaveform(
+ /* timings= */ new long[] {5},
+ /* repeatIndex= */ -1))
+ .repeatEffectIndefinitely(
+ VibrationEffect.createWaveform(
+ /* timings= */ new long[] {2, 3},
+ /* repeatIndex= */ -1))
+ .compose();
+
+ assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
+ public void computeLegacyPattern_effectsViaStartWaveform() {
+ // Effects created via startWaveform are not expected to be converted to long[] patterns, as
+ // they are not configured to always play with the default amplitude.
+ VibrationEffect effect = VibrationEffect.startWaveform(targetFrequency(60))
+ .addTransition(Duration.ofMillis(100), targetAmplitude(1), targetFrequency(120))
+ .addSustain(Duration.ofMillis(200))
+ .addTransition(Duration.ofMillis(100), targetAmplitude(0), targetFrequency(60))
+ .build();
+
+ assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
+
+ effect = VibrationEffect.startWaveform(targetFrequency(60))
+ .addTransition(Duration.ofMillis(80), targetAmplitude(1))
+ .addSustain(Duration.ofMillis(200))
+ .addTransition(Duration.ofMillis(100), targetAmplitude(0))
+ .build();
+
+ assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
+
+ effect = VibrationEffect.startWaveform(targetFrequency(60))
+ .addTransition(Duration.ofMillis(100), targetFrequency(50))
+ .addSustain(Duration.ofMillis(50))
+ .addTransition(Duration.ofMillis(20), targetFrequency(75))
+ .build();
+
+ assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
+ }
+
+ @Test
public void getRingtones_noPrebakedRingtones() {
Resources r = mockRingtoneResources(new String[0]);
Context context = mockContext(r);
@@ -100,7 +449,7 @@
@Test
public void testValidateOneShot() {
VibrationEffect.createOneShot(1, 255).validate();
- VibrationEffect.createOneShot(1, VibrationEffect.DEFAULT_AMPLITUDE).validate();
+ VibrationEffect.createOneShot(1, DEFAULT_AMPLITUDE).validate();
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.createOneShot(-1, 255).validate());
@@ -501,6 +850,13 @@
assertTrue(VibrationEffect.get(VibrationEffect.EFFECT_TICK).isHapticFeedbackCandidate());
}
+ private void assertArrayEq(long[] expected, long[] actual) {
+ assertTrue(
+ String.format("Expected pattern %s, but was %s",
+ Arrays.toString(expected), Arrays.toString(actual)),
+ Arrays.equals(expected, actual));
+ }
+
private Resources mockRingtoneResources() {
return mockRingtoneResources(new String[]{
RINGTONE_URI_1,
diff --git a/graphics/java/android/view/PixelCopy.java b/graphics/java/android/view/PixelCopy.java
index 82ced43..0e198d5 100644
--- a/graphics/java/android/view/PixelCopy.java
+++ b/graphics/java/android/view/PixelCopy.java
@@ -382,9 +382,9 @@
}
/**
- * Creates a PixelCopy request for the given {@link Window}
+ * Creates a PixelCopy Builder for the given {@link Window}
* @param source The Window to copy from
- * @return A {@link Builder} builder to set the optional params & execute the request
+ * @return A {@link Builder} builder to set the optional params & build the request
*/
@SuppressLint("BuilderSetStyle")
public static @NonNull Builder ofWindow(@NonNull Window source) {
@@ -394,7 +394,7 @@
}
/**
- * Creates a PixelCopy request for the {@link Window} that the given {@link View} is
+ * Creates a PixelCopy Builder for the {@link Window} that the given {@link View} is
* attached to.
*
* Note that this copy request is not cropped to the area the View occupies by default.
@@ -404,7 +404,7 @@
*
* @param source A View that {@link View#isAttachedToWindow() is attached} to a window
* that will be used to retrieve the window to copy from.
- * @return A {@link Builder} builder to set the optional params & execute the request
+ * @return A {@link Builder} builder to set the optional params & build the request
*/
@SuppressLint("BuilderSetStyle")
public static @NonNull Builder ofWindow(@NonNull View source) {
@@ -427,10 +427,10 @@
}
/**
- * Creates a PixelCopy request for the given {@link Surface}
+ * Creates a PixelCopy Builder for the given {@link Surface}
*
* @param source The Surface to copy from. Must be {@link Surface#isValid() valid}.
- * @return A {@link Builder} builder to set the optional params & execute the request
+ * @return A {@link Builder} builder to set the optional params & build the request
*/
@SuppressLint("BuilderSetStyle")
public static @NonNull Builder ofSurface(@NonNull Surface source) {
@@ -441,12 +441,12 @@
}
/**
- * Creates a PixelCopy request for the {@link Surface} belonging to the
+ * Creates a PixelCopy Builder for the {@link Surface} belonging to the
* given {@link SurfaceView}
*
* @param source The SurfaceView to copy from. The backing surface must be
* {@link Surface#isValid() valid}
- * @return A {@link Builder} builder to set the optional params & execute the request
+ * @return A {@link Builder} builder to set the optional params & build the request
*/
@SuppressLint("BuilderSetStyle")
public static @NonNull Builder ofSurface(@NonNull SurfaceView source) {
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 9aa3787..c0fa63a 100755
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -83,15 +83,16 @@
return {};
}
+ std::string overlay_path(loaded_idmap->OverlayApkPath());
+ auto fd = unique_fd(::open(overlay_path.c_str(), O_RDONLY|O_CLOEXEC));
std::unique_ptr<AssetsProvider> overlay_assets;
- const std::string overlay_path(loaded_idmap->OverlayApkPath());
- if (IsFabricatedOverlay(overlay_path)) {
+ if (IsFabricatedOverlay(fd)) {
// Fabricated overlays do not contain resource definitions. All of the overlay resource values
// are defined inline in the idmap.
- overlay_assets = EmptyAssetsProvider::Create(overlay_path);
+ overlay_assets = EmptyAssetsProvider::Create(std::move(overlay_path));
} else {
// The overlay should be an APK.
- overlay_assets = ZipAssetsProvider::Create(overlay_path, flags);
+ overlay_assets = ZipAssetsProvider::Create(std::move(fd), std::move(overlay_path), flags);
}
if (overlay_assets == nullptr) {
return {};
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 61e842a..c3d153d 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -112,7 +112,7 @@
// A mapping from path of apk assets that could be target packages of overlays to the runtime
// package id of its first loaded package. Overlays currently can only override resources in the
// first package in the target resource table.
- std::unordered_map<std::string, uint8_t> target_assets_package_ids;
+ std::unordered_map<std::string_view, uint8_t> target_assets_package_ids;
// Overlay resources are not directly referenced by an application so their resource ids
// can change throughout the application's lifetime. Assign overlay package ids last.
@@ -135,7 +135,7 @@
if (auto loaded_idmap = apk_assets->GetLoadedIdmap(); loaded_idmap != nullptr) {
// The target package must precede the overlay package in the apk assets paths in order
// to take effect.
- auto iter = target_assets_package_ids.find(std::string(loaded_idmap->TargetApkPath()));
+ auto iter = target_assets_package_ids.find(loaded_idmap->TargetApkPath());
if (iter == target_assets_package_ids.end()) {
LOG(INFO) << "failed to find target package for overlay "
<< loaded_idmap->OverlayApkPath();
@@ -180,7 +180,7 @@
if (overlay_ref_table != nullptr) {
// If this package is from an overlay, use a dynamic reference table that can rewrite
// overlay resource ids to their corresponding target resource ids.
- new_group.dynamic_ref_table = overlay_ref_table;
+ new_group.dynamic_ref_table = std::move(overlay_ref_table);
}
DynamicRefTable* ref_table = new_group.dynamic_ref_table.get();
@@ -188,9 +188,9 @@
ref_table->mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
}
- // Add the package and to the set of packages with the same ID.
+ // Add the package to the set of packages with the same ID.
PackageGroup* package_group = &package_groups_[idx];
- package_group->packages_.push_back(ConfiguredPackage{package.get(), {}});
+ package_group->packages_.emplace_back().loaded_package_ = package.get();
package_group->cookies_.push_back(apk_assets_cookies[apk_assets]);
// Add the package name -> build time ID mappings.
@@ -202,7 +202,7 @@
if (auto apk_assets_path = apk_assets->GetPath()) {
// Overlay target ApkAssets must have been created using path based load apis.
- target_assets_package_ids.insert(std::make_pair(std::string(*apk_assets_path), package_id));
+ target_assets_package_ids.emplace(*apk_assets_path, package_id);
}
}
}
@@ -219,11 +219,13 @@
for (const auto& package : group.packages_) {
const auto& package_aliases = package.loaded_package_->GetAliasResourceIdMap();
- aliases.insert(package_aliases.begin(), package_aliases.end());
+ aliases.insert(aliases.end(), package_aliases.begin(), package_aliases.end());
}
}
if (!aliases.empty()) {
+ std::sort(aliases.begin(), aliases.end(), [](auto&& l, auto&& r) { return l.first < r.first; });
+
// Add the alias resources to the dynamic reference table of every package group. Since
// staging aliases can only be defined by the framework package (which is not a shared
// library), the compile-time package id of the framework is the same across all packages
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
index 289d7e6..80e5607 100644
--- a/libs/androidfw/AssetsProvider.cpp
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -393,8 +393,8 @@
return std::unique_ptr<EmptyAssetsProvider>(new EmptyAssetsProvider({}));
}
-std::unique_ptr<AssetsProvider> EmptyAssetsProvider::Create(const std::string& path) {
- return std::unique_ptr<EmptyAssetsProvider>(new EmptyAssetsProvider(path));
+std::unique_ptr<AssetsProvider> EmptyAssetsProvider::Create(std::string path) {
+ return std::unique_ptr<EmptyAssetsProvider>(new EmptyAssetsProvider(std::move(path)));
}
std::unique_ptr<Asset> EmptyAssetsProvider::OpenInternal(const std::string& /* path */,
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 035ed4f..31516dc 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -33,7 +33,9 @@
#include <type_traits>
#include <vector>
+#include <android-base/file.h>
#include <android-base/macros.h>
+#include <android-base/utf8.h>
#include <androidfw/ByteBucketArray.h>
#include <androidfw/ResourceTypes.h>
#include <androidfw/TypeWrappers.h>
@@ -236,12 +238,23 @@
}
bool IsFabricatedOverlay(const std::string& path) {
- std::ifstream fin(path);
- uint32_t magic;
- if (fin.read(reinterpret_cast<char*>(&magic), sizeof(uint32_t))) {
- return magic == kFabricatedOverlayMagic;
+ return IsFabricatedOverlay(path.c_str());
+}
+
+bool IsFabricatedOverlay(const char* path) {
+ auto fd = base::unique_fd(base::utf8::open(path, O_RDONLY|O_CLOEXEC));
+ if (fd < 0) {
+ return false;
}
- return false;
+ return IsFabricatedOverlay(fd);
+}
+
+bool IsFabricatedOverlay(base::borrowed_fd fd) {
+ uint32_t magic;
+ if (!base::ReadFullyAtOffset(fd, &magic, sizeof(magic), 0)) {
+ return false;
+ }
+ return magic == kFabricatedOverlayMagic;
}
static bool assertIdmapHeader(const void* idmap, size_t size) {
@@ -6988,11 +7001,10 @@
DynamicRefTable::DynamicRefTable() : DynamicRefTable(0, false) {}
DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib)
- : mAssignedPackageId(packageId)
+ : mLookupTable()
+ , mAssignedPackageId(packageId)
, mAppAsLib(appAsLib)
{
- memset(mLookupTable, 0, sizeof(mLookupTable));
-
// Reserved package ids
mLookupTable[APP_PACKAGE_ID] = APP_PACKAGE_ID;
mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID;
@@ -7073,10 +7085,6 @@
mLookupTable[buildPackageId] = runtimePackageId;
}
-void DynamicRefTable::addAlias(uint32_t stagedId, uint32_t finalizedId) {
- mAliasId[stagedId] = finalizedId;
-}
-
status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
uint32_t res = *resId;
size_t packageId = Res_GETPACKAGE(res) + 1;
@@ -7086,11 +7094,12 @@
return NO_ERROR;
}
- auto alias_id = mAliasId.find(res);
- if (alias_id != mAliasId.end()) {
+ const auto alias_it = std::lower_bound(mAliasId.begin(), mAliasId.end(), res,
+ [](const AliasMap::value_type& pair, uint32_t val) { return pair.first < val; });
+ if (alias_it != mAliasId.end() && alias_it->first == res) {
// Rewrite the resource id to its alias resource id. Since the alias resource id is a
// compile-time id, it still needs to be resolved further.
- res = alias_id->second;
+ res = alias_it->second;
}
if (packageId == SYS_PACKAGE_ID || (packageId == APP_PACKAGE_ID && !mAppAsLib)) {
diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h
index 13cbe3b..af6e7f4 100644
--- a/libs/androidfw/include/androidfw/AssetsProvider.h
+++ b/libs/androidfw/include/androidfw/AssetsProvider.h
@@ -181,7 +181,7 @@
// Does not provide any assets.
struct EmptyAssetsProvider : public AssetsProvider {
static std::unique_ptr<AssetsProvider> Create();
- static std::unique_ptr<AssetsProvider> Create(const std::string& path);
+ static std::unique_ptr<AssetsProvider> Create(std::string path);
bool ForEachFile(const std::string& path,
const std::function<void(StringPiece, FileType)>& f) const override;
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index d98e97a..52321da 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -21,6 +21,7 @@
#define _LIBS_UTILS_RESOURCE_TYPES_H
#include <android-base/expected.h>
+#include <android-base/unique_fd.h>
#include <androidfw/Asset.h>
#include <androidfw/Errors.h>
@@ -58,6 +59,8 @@
// Returns whether or not the path represents a fabricated overlay.
bool IsFabricatedOverlay(const std::string& path);
+bool IsFabricatedOverlay(const char* path);
+bool IsFabricatedOverlay(android::base::borrowed_fd fd);
/**
* In C++11, char16_t is defined as *at least* 16 bits. We do a lot of
@@ -1882,11 +1885,10 @@
void addMapping(uint8_t buildPackageId, uint8_t runtimePackageId);
- using AliasMap = std::map<uint32_t, uint32_t>;
+ using AliasMap = std::vector<std::pair<uint32_t, uint32_t>>;
void setAliases(AliasMap aliases) {
mAliasId = std::move(aliases);
}
- void addAlias(uint32_t stagedId, uint32_t finalizedId);
// Returns whether or not the value must be looked up.
bool requiresLookup(const Res_value* value) const;
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index 252ecf4..8c9023c 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -49,6 +49,8 @@
<string name="save_password_to_title">Save password to</string>
<!-- This appears as the title of the modal bottom sheet for users to choose other available places the created other credential types can be saved to. [CHAR LIMIT=200] -->
<string name="save_sign_in_to_title">Save sign-in to</string>
+ <!-- This appears as the title of the modal bottom sheet for users to choose to create a passkey on another device. [CHAR LIMIT=200] -->
+ <string name="create_passkey_in_other_device_title">Create a passkey in another device?</string>
<!-- This appears as the title of the modal bottom sheet for users to confirm whether they should use the selected provider as default or not. [CHAR LIMIT=200] -->
<string name="use_provider_for_all_title">Use <xliff:g id="providerInfoDisplayName" example="Google Password Manager">%1$s</xliff:g> for all your sign-ins?</string>
<!-- TODO: Check the wording here. -->
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 530f1c4..0cc1194 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -44,6 +44,8 @@
import com.android.credentialmanager.createflow.CreateCredentialUiState
import com.android.credentialmanager.createflow.CreateScreenState
import com.android.credentialmanager.createflow.EnabledProviderInfo
+import com.android.credentialmanager.createflow.RemoteInfo
+import com.android.credentialmanager.createflow.RequestDisplayInfo
import com.android.credentialmanager.getflow.GetCredentialUiState
import com.android.credentialmanager.getflow.GetScreenState
import com.android.credentialmanager.jetpack.developer.CreatePasswordRequest.Companion.toBundle
@@ -142,25 +144,22 @@
val providerDisabledList = CreateFlowUtils.toDisabledProviderList(
// Handle runtime cast error
providerDisabledList as List<DisabledProviderData>, context)
- var hasDefault = false
- var defaultProvider: EnabledProviderInfo = providerEnabledList.first()
+ var defaultProvider: EnabledProviderInfo? = null
+ var remoteEntry: RemoteInfo? = null
providerEnabledList.forEach{providerInfo -> providerInfo.createOptions =
providerInfo.createOptions.sortedWith(compareBy { it.lastUsedTimeMillis }).reversed()
- if (providerInfo.isDefault) {hasDefault = true; defaultProvider = providerInfo} }
+ if (providerInfo.isDefault) {defaultProvider = providerInfo}
+ if (providerInfo.remoteEntry != null) {
+ remoteEntry = providerInfo.remoteEntry!!
+ }
+ }
return CreateCredentialUiState(
enabledProviders = providerEnabledList,
disabledProviders = providerDisabledList,
- // TODO: Add the screen when defaultProvider has no createOption but
- // there's remoteInfo under other providers
- if (!hasDefault || defaultProvider.createOptions.isEmpty()) {
- if (requestDisplayInfo.type == TYPE_PUBLIC_KEY_CREDENTIAL)
- {CreateScreenState.PASSKEY_INTRO} else {CreateScreenState.PROVIDER_SELECTION}
- } else {CreateScreenState.CREATION_OPTION_SELECTION},
+ toCreateScreenState(requestDisplayInfo, defaultProvider, remoteEntry),
requestDisplayInfo,
false,
- if (hasDefault) {
- ActiveEntry(defaultProvider, defaultProvider.createOptions.first())
- } else null
+ toActiveEntry(defaultProvider, remoteEntry),
)
}
@@ -509,4 +508,38 @@
"tribank.us"
)
}
+
+ private fun toCreateScreenState(
+ requestDisplayInfo: RequestDisplayInfo,
+ defaultProvider: EnabledProviderInfo?,
+ remoteEntry: RemoteInfo?,
+ ): CreateScreenState {
+ return if (
+ defaultProvider != null && defaultProvider.createOptions.isEmpty() && remoteEntry != null
+ ){
+ CreateScreenState.EXTERNAL_ONLY_SELECTION
+ } else if (defaultProvider == null || defaultProvider.createOptions.isEmpty()) {
+ if (requestDisplayInfo.type == TYPE_PUBLIC_KEY_CREDENTIAL) {
+ CreateScreenState.PASSKEY_INTRO
+ } else {
+ CreateScreenState.PROVIDER_SELECTION
+ }
+ } else {
+ CreateScreenState.CREATION_OPTION_SELECTION
+ }
+ }
+
+ private fun toActiveEntry(
+ defaultProvider: EnabledProviderInfo?,
+ remoteEntry: RemoteInfo?,
+ ): ActiveEntry? {
+ return if (
+ defaultProvider != null && defaultProvider.createOptions.isNotEmpty()
+ ) {
+ ActiveEntry(defaultProvider, defaultProvider.createOptions.first())
+ } else if (
+ defaultProvider != null && defaultProvider.createOptions.isEmpty() && remoteEntry != null) {
+ ActiveEntry(defaultProvider, remoteEntry)
+ } else null
+ }
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 0e36b3a..5552d05 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -60,7 +60,7 @@
viewModel.onEntrySelected(it, providerActivityLauncher)
}
val confirmEntryCallback: () -> Unit = {
- viewModel.onConfirmCreationSelected(providerActivityLauncher)
+ viewModel.onConfirmEntrySelected(providerActivityLauncher)
}
val state = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Expanded,
@@ -108,6 +108,13 @@
providerInfo = uiState.activeEntry?.activeProvider!!,
onDefaultOrNotSelected = viewModel::onDefaultOrNotSelected
)
+ CreateScreenState.EXTERNAL_ONLY_SELECTION -> ExternalOnlySelectionCard(
+ requestDisplayInfo = uiState.requestDisplayInfo,
+ activeRemoteEntry = uiState.activeEntry?.activeEntryInfo!!,
+ onOptionSelected = selectEntryCallback,
+ onConfirm = confirmEntryCallback,
+ onCancel = viewModel::onCancel,
+ )
}
},
scrimColor = MaterialTheme.colorScheme.scrim.copy(alpha = 0.8f),
@@ -489,7 +496,7 @@
) {
PrimaryCreateOptionRow(
requestDisplayInfo = requestDisplayInfo,
- createOptionInfo = createOptionInfo,
+ entryInfo = createOptionInfo,
onOptionSelected = onOptionSelected
)
}
@@ -562,16 +569,85 @@
@OptIn(ExperimentalMaterial3Api::class)
@Composable
+fun ExternalOnlySelectionCard(
+ requestDisplayInfo: RequestDisplayInfo,
+ activeRemoteEntry: EntryInfo,
+ onOptionSelected: (EntryInfo) -> Unit,
+ onConfirm: () -> Unit,
+ onCancel: () -> Unit,
+) {
+ Card() {
+ Column() {
+ Icon(
+ painter = painterResource(R.drawable.ic_other_devices),
+ contentDescription = null,
+ tint = Color.Unspecified,
+ modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
+ .padding(all = 24.dp).size(32.dp)
+ )
+ Text(
+ text = stringResource(R.string.create_passkey_in_other_device_title),
+ style = MaterialTheme.typography.titleMedium,
+ modifier = Modifier.padding(horizontal = 24.dp)
+ .align(alignment = Alignment.CenterHorizontally),
+ textAlign = TextAlign.Center,
+ )
+ Divider(
+ thickness = 24.dp,
+ color = Color.Transparent
+ )
+ Card(
+ shape = MaterialTheme.shapes.medium,
+ modifier = Modifier
+ .padding(horizontal = 24.dp)
+ .align(alignment = Alignment.CenterHorizontally),
+ ) {
+ PrimaryCreateOptionRow(
+ requestDisplayInfo = requestDisplayInfo,
+ entryInfo = activeRemoteEntry,
+ onOptionSelected = onOptionSelected
+ )
+ }
+ Divider(
+ thickness = 24.dp,
+ color = Color.Transparent
+ )
+ Row(
+ horizontalArrangement = Arrangement.SpaceBetween,
+ modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
+ ) {
+ CancelButton(
+ stringResource(R.string.string_cancel),
+ onClick = onCancel
+ )
+ ConfirmButton(
+ stringResource(R.string.string_continue),
+ onClick = onConfirm
+ )
+ }
+ Divider(
+ thickness = 18.dp,
+ color = Color.Transparent,
+ modifier = Modifier.padding(bottom = 16.dp)
+ )
+ }
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
fun PrimaryCreateOptionRow(
requestDisplayInfo: RequestDisplayInfo,
- createOptionInfo: CreateOptionInfo,
+ entryInfo: EntryInfo,
onOptionSelected: (EntryInfo) -> Unit
) {
Entry(
- onClick = {onOptionSelected(createOptionInfo)},
+ onClick = {onOptionSelected(entryInfo)},
icon = {
Icon(
- bitmap = createOptionInfo.profileIcon.toBitmap().asImageBitmap(),
+ bitmap = if (entryInfo is CreateOptionInfo) {
+ entryInfo.profileIcon.toBitmap().asImageBitmap()
+ } else {requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()},
contentDescription = null,
tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
modifier = Modifier.padding(start = 18.dp).size(32.dp)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
index 0f685a1..393cf7d 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
@@ -155,7 +155,7 @@
}
}
- fun onConfirmCreationSelected(
+ fun onConfirmEntrySelected(
launcher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
) {
val selectedEntry = uiState.activeEntry?.activeEntryInfo
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
index 31d0365..9ac524a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
@@ -95,4 +95,5 @@
CREATION_OPTION_SELECTION,
MORE_OPTIONS_SELECTION,
MORE_OPTIONS_ROW_INTRO,
+ EXTERNAL_ONLY_SELECTION,
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt
index 0c9a043..238204a 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt
@@ -36,6 +36,7 @@
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.framework.util.createIntent
import com.android.settingslib.spa.gallery.R
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.ASYNC_PREFERENCE_SUMMARY
@@ -91,6 +92,7 @@
spaLogger.message(TAG, "create macro for ${EntryEnum.SIMPLE_PREFERENCE}")
SimplePreferenceMacro(title = SIMPLE_PREFERENCE_TITLE)
}
+ .setStatusDataFn { EntryStatusData(isDisabled = false) }
.build()
)
entryList.add(
@@ -103,6 +105,7 @@
searchKeywords = SIMPLE_PREFERENCE_KEYWORDS,
)
}
+ .setStatusDataFn { EntryStatusData(isDisabled = true) }
.build()
)
entryList.add(singleLineSummaryEntry())
@@ -269,7 +272,7 @@
)
}
.setSliceDataFn { sliceUri, _ ->
- val intent = owner.createBrowseIntent()?.createBrowsePendingIntent()
+ val intent = owner.createIntent()?.createBrowsePendingIntent()
?: return@setSliceDataFn null
return@setSliceDataFn object : EntrySliceData() {
init {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/DebugActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/DebugActivity.kt
index 238268a..f7cbdae 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/DebugActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/DebugActivity.kt
@@ -39,7 +39,10 @@
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.theme.SettingsTheme
-import com.android.settingslib.spa.slice.appendSliceParams
+import com.android.settingslib.spa.framework.util.SESSION_BROWSE
+import com.android.settingslib.spa.framework.util.SESSION_SEARCH
+import com.android.settingslib.spa.framework.util.createIntent
+import com.android.settingslib.spa.slice.fromEntry
import com.android.settingslib.spa.slice.presenter.SliceDemo
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
@@ -158,14 +161,13 @@
remember { entryRepository.getAllEntries().filter { it.hasSliceSupport } }
RegularScaffold(title = "All Slices (${allSliceEntry.size})") {
for (entry in allSliceEntry) {
- SliceDemo(sliceUri = entry.createSliceUri(authority))
+ SliceDemo(sliceUri = Uri.Builder().fromEntry(entry, authority).build())
}
}
}
@Composable
fun OnePage(arguments: Bundle?) {
- val context = LocalContext.current
val entryRepository by spaEnvironment.entryRepository
val id = arguments!!.getString(PARAM_NAME_PAGE_ID, "")
val pageWithEntry = entryRepository.getPageWithEntry(id)!!
@@ -176,8 +178,8 @@
Text(text = "Entry size: ${pageWithEntry.entries.size}")
Preference(model = object : PreferenceModel {
override val title = "open page"
- override val enabled =
- page.isBrowsable(context, spaEnvironment.browseActivityClass).toState()
+ override val enabled = (spaEnvironment.browseActivityClass != null &&
+ page.isBrowsable()).toState()
override val onClick = openPage(page)
})
EntryList(pageWithEntry.entries)
@@ -186,7 +188,6 @@
@Composable
fun OneEntry(arguments: Bundle?) {
- val context = LocalContext.current
val entryRepository by spaEnvironment.entryRepository
val id = arguments!!.getString(PARAM_NAME_ENTRY_ID, "")
val entry = entryRepository.getEntry(id)!!
@@ -194,9 +195,9 @@
RegularScaffold(title = "Entry - ${entry.debugBrief()}") {
Preference(model = object : PreferenceModel {
override val title = "open entry"
- override val enabled =
- entry.containerPage().isBrowsable(context, spaEnvironment.browseActivityClass)
- .toState()
+ override val enabled = (spaEnvironment.browseActivityClass != null &&
+ entry.containerPage().isBrowsable())
+ .toState()
override val onClick = openEntry(entry)
})
Text(text = entryContent)
@@ -219,7 +220,7 @@
private fun openPage(page: SettingsPage): (() -> Unit)? {
val context = LocalContext.current
val intent =
- page.createBrowseIntent(context, spaEnvironment.browseActivityClass) ?: return null
+ page.createIntent(SESSION_BROWSE) ?: return null
val route = page.buildRoute()
return {
spaEnvironment.logger.message(
@@ -232,8 +233,7 @@
@Composable
private fun openEntry(entry: SettingsEntry): (() -> Unit)? {
val context = LocalContext.current
- val intent = entry.containerPage()
- .createBrowseIntent(context, spaEnvironment.browseActivityClass, entry.id)
+ val intent = entry.createIntent(SESSION_SEARCH)
?: return null
val route = entry.containerPage().buildRoute()
return {
@@ -245,18 +245,6 @@
}
}
-private fun SettingsEntry.createSliceUri(
- authority: String?,
- runtimeArguments: Bundle? = null
-): Uri {
- if (authority == null) return Uri.EMPTY
- return Uri.Builder().scheme("content").authority(authority).appendSliceParams(
- route = this.containerPage().buildRoute(),
- entryId = this.id,
- runtimeArguments = runtimeArguments,
- ).build()
-}
-
/**
* A blank activity without any page.
*/
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/DebugProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/DebugProvider.kt
index 3df7727..59ec985 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/DebugProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/DebugProvider.kt
@@ -32,6 +32,12 @@
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.common.addUri
import com.android.settingslib.spa.framework.common.getColumns
+import com.android.settingslib.spa.framework.util.KEY_DESTINATION
+import com.android.settingslib.spa.framework.util.KEY_HIGHLIGHT_ENTRY
+import com.android.settingslib.spa.framework.util.KEY_SESSION_SOURCE_NAME
+import com.android.settingslib.spa.framework.util.SESSION_BROWSE
+import com.android.settingslib.spa.framework.util.SESSION_SEARCH
+import com.android.settingslib.spa.framework.util.createIntent
private const val TAG = "DebugProvider"
@@ -116,9 +122,11 @@
val entryRepository by spaEnvironment.entryRepository
val cursor = MatrixCursor(QueryEnum.PAGE_DEBUG_QUERY.getColumns())
for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
- val command = pageWithEntry.page.createBrowseAdbCommand(
- context,
- spaEnvironment.browseActivityClass
+ val page = pageWithEntry.page
+ if (!page.isBrowsable()) continue
+ val command = createBrowseAdbCommand(
+ destination = page.buildRoute(),
+ sessionName = SESSION_BROWSE
)
if (command != null) {
cursor.newRow().add(ColumnEnum.PAGE_START_ADB.id, command)
@@ -131,8 +139,13 @@
val entryRepository by spaEnvironment.entryRepository
val cursor = MatrixCursor(QueryEnum.ENTRY_DEBUG_QUERY.getColumns())
for (entry in entryRepository.getAllEntries()) {
- val command = entry.containerPage()
- .createBrowseAdbCommand(context, spaEnvironment.browseActivityClass, entry.id)
+ val page = entry.containerPage()
+ if (!page.isBrowsable()) continue
+ val command = createBrowseAdbCommand(
+ destination = page.buildRoute(),
+ entryId = entry.id,
+ sessionName = SESSION_SEARCH
+ )
if (command != null) {
cursor.newRow().add(ColumnEnum.ENTRY_START_ADB.id, command)
}
@@ -145,8 +158,7 @@
val cursor = MatrixCursor(QueryEnum.PAGE_INFO_QUERY.getColumns())
for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
val page = pageWithEntry.page
- val intent =
- page.createBrowseIntent(context, spaEnvironment.browseActivityClass) ?: Intent()
+ val intent = page.createIntent(SESSION_BROWSE) ?: Intent()
cursor.newRow()
.add(ColumnEnum.PAGE_ID.id, page.id)
.add(ColumnEnum.PAGE_NAME.id, page.displayName)
@@ -162,17 +174,36 @@
val entryRepository by spaEnvironment.entryRepository
val cursor = MatrixCursor(QueryEnum.ENTRY_INFO_QUERY.getColumns())
for (entry in entryRepository.getAllEntries()) {
- val intent = entry.containerPage()
- .createBrowseIntent(context, spaEnvironment.browseActivityClass, entry.id)
- ?: Intent()
+ val intent = entry.createIntent(SESSION_SEARCH) ?: Intent()
cursor.newRow()
.add(ColumnEnum.ENTRY_ID.id, entry.id)
.add(ColumnEnum.ENTRY_NAME.id, entry.displayName)
.add(ColumnEnum.ENTRY_ROUTE.id, entry.containerPage().buildRoute())
.add(ColumnEnum.ENTRY_INTENT_URI.id, intent.toUri(URI_INTENT_SCHEME))
- .add(ColumnEnum.ENTRY_HIERARCHY_PATH.id,
- entryRepository.getEntryPathWithDisplayName(entry.id))
+ .add(
+ ColumnEnum.ENTRY_HIERARCHY_PATH.id,
+ entryRepository.getEntryPathWithDisplayName(entry.id)
+ )
}
return cursor
}
}
+
+private fun createBrowseAdbCommand(
+ destination: String? = null,
+ entryId: String? = null,
+ sessionName: String? = null,
+): String? {
+ val context = SpaEnvironmentFactory.instance.appContext
+ val browseActivityClass = SpaEnvironmentFactory.instance.browseActivityClass ?: return null
+ val packageName = context.packageName
+ val activityName = browseActivityClass.name.replace(packageName, "")
+ val destinationParam =
+ if (destination != null) " -e $KEY_DESTINATION $destination" else ""
+ val highlightParam =
+ if (entryId != null) " -e $KEY_HIGHLIGHT_ENTRY $entryId" else ""
+ val sessionParam =
+ if (sessionName != null) " -e $KEY_SESSION_SOURCE_NAME $sessionName" else ""
+ return "adb shell am start -n $packageName/$activityName" +
+ "$destinationParam$highlightParam$sessionParam"
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index cd3ec96..aa10cc8 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -42,6 +42,9 @@
import com.android.settingslib.spa.framework.compose.localNavController
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.util.PageEvent
+import com.android.settingslib.spa.framework.util.getDestination
+import com.android.settingslib.spa.framework.util.getEntryId
+import com.android.settingslib.spa.framework.util.getSessionName
import com.android.settingslib.spa.framework.util.navRoute
private const val TAG = "BrowseActivity"
@@ -78,12 +81,6 @@
}
}
}
-
- companion object {
- const val KEY_DESTINATION = "spaActivityDestination"
- const val KEY_HIGHLIGHT_ENTRY = "highlightEntry"
- const val KEY_SESSION_SOURCE_NAME = "sessionSource"
- }
}
@VisibleForTesting
@@ -126,11 +123,10 @@
if (destinationNavigated.value) return
destinationNavigated.value = true
- val initialDestination = initialIntent?.getStringExtra(BrowseActivity.KEY_DESTINATION)
- ?: defaultDestination
+ val initialDestination = initialIntent?.getDestination() ?: defaultDestination
if (initialDestination.isEmpty()) return
- val initialEntryId = initialIntent?.getStringExtra(BrowseActivity.KEY_HIGHLIGHT_ENTRY)
- val sessionSourceName = initialIntent?.getStringExtra(BrowseActivity.KEY_SESSION_SOURCE_NAME)
+ val initialEntryId = initialIntent?.getEntryId()
+ val sessionSourceName = initialIntent?.getSessionName()
LaunchedEffect(Unit) {
highlightId = initialEntryId
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt
index bc5dca8..7a39b73 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt
@@ -16,13 +16,8 @@
package com.android.settingslib.spa.framework.common
-import android.app.Activity
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
import android.os.Bundle
import androidx.navigation.NamedNavArgument
-import com.android.settingslib.spa.framework.BrowseActivity
import com.android.settingslib.spa.framework.util.isRuntimeParam
import com.android.settingslib.spa.framework.util.navLink
import com.android.settingslib.spa.framework.util.normalize
@@ -95,45 +90,8 @@
return false
}
- fun createBrowseIntent(entryId: String? = null): Intent? {
- val context = SpaEnvironmentFactory.instance.appContext
- val browseActivityClass = SpaEnvironmentFactory.instance.browseActivityClass
- return createBrowseIntent(context, browseActivityClass, entryId)
- }
-
- fun createBrowseIntent(
- context: Context?,
- browseActivityClass: Class<out Activity>?,
- entryId: String? = null
- ): Intent? {
- if (!isBrowsable(context, browseActivityClass)) return null
- return Intent().setComponent(ComponentName(context!!, browseActivityClass!!))
- .apply {
- putExtra(BrowseActivity.KEY_DESTINATION, buildRoute())
- if (entryId != null) {
- putExtra(BrowseActivity.KEY_HIGHLIGHT_ENTRY, entryId)
- }
- }
- }
-
- fun createBrowseAdbCommand(
- context: Context?,
- browseActivityClass: Class<out Activity>?,
- entryId: String? = null
- ): String? {
- if (!isBrowsable(context, browseActivityClass)) return null
- val packageName = context!!.packageName
- val activityName = browseActivityClass!!.name.replace(packageName, "")
- val destinationParam = " -e ${BrowseActivity.KEY_DESTINATION} ${buildRoute()}"
- val highlightParam =
- if (entryId != null) " -e ${BrowseActivity.KEY_HIGHLIGHT_ENTRY} $entryId" else ""
- return "adb shell am start -n $packageName/$activityName$destinationParam$highlightParam"
- }
-
- fun isBrowsable(context: Context?, browseActivityClass: Class<out Activity>?): Boolean {
- return context != null &&
- browseActivityClass != null &&
- !isCreateBy(NULL_PAGE_NAME) &&
+ fun isBrowsable(): Boolean {
+ return !isCreateBy(NULL_PAGE_NAME) &&
!hasRuntimeParam()
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/SpaIntent.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/SpaIntent.kt
new file mode 100644
index 0000000..2c3c2e0
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/SpaIntent.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.settingslib.spa.framework.util
+
+import android.content.ComponentName
+import android.content.Intent
+import com.android.settingslib.spa.framework.common.SettingsEntry
+import com.android.settingslib.spa.framework.common.SettingsPage
+import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
+
+const val SESSION_BROWSE = "browse"
+const val SESSION_SEARCH = "search"
+const val SESSION_SLICE = "slice"
+
+const val KEY_DESTINATION = "spaActivityDestination"
+const val KEY_HIGHLIGHT_ENTRY = "highlightEntry"
+const val KEY_SESSION_SOURCE_NAME = "sessionSource"
+
+val SPA_INTENT_RESERVED_KEYS = listOf(
+ KEY_DESTINATION,
+ KEY_HIGHLIGHT_ENTRY,
+ KEY_SESSION_SOURCE_NAME
+)
+
+private fun createBaseIntent(): Intent? {
+ val context = SpaEnvironmentFactory.instance.appContext
+ val browseActivityClass = SpaEnvironmentFactory.instance.browseActivityClass ?: return null
+ return Intent().setComponent(ComponentName(context, browseActivityClass))
+}
+
+fun SettingsPage.createIntent(sessionName: String? = null): Intent? {
+ if (!isBrowsable()) return null
+ return createBaseIntent()?.appendSpaParams(
+ destination = buildRoute(),
+ sessionName = sessionName
+ )
+}
+
+fun SettingsEntry.createIntent(sessionName: String? = null): Intent? {
+ val sp = containerPage()
+ if (!sp.isBrowsable()) return null
+ return createBaseIntent()?.appendSpaParams(
+ destination = sp.buildRoute(),
+ entryId = id,
+ sessionName = sessionName
+ )
+}
+
+fun Intent.appendSpaParams(
+ destination: String? = null,
+ entryId: String? = null,
+ sessionName: String? = null
+): Intent {
+ return apply {
+ if (destination != null) putExtra(KEY_DESTINATION, destination)
+ if (entryId != null) putExtra(KEY_HIGHLIGHT_ENTRY, entryId)
+ if (sessionName != null) putExtra(KEY_SESSION_SOURCE_NAME, sessionName)
+ }
+}
+
+fun Intent.getDestination(): String? {
+ return getStringExtra(KEY_DESTINATION)
+}
+
+fun Intent.getEntryId(): String? {
+ return getStringExtra(KEY_HIGHLIGHT_ENTRY)
+}
+
+fun Intent.getSessionName(): String? {
+ return getStringExtra(KEY_SESSION_SOURCE_NAME)
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/search/SpaSearchProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/search/SpaSearchProvider.kt
index 7f2f4fd..02aed1c 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/search/SpaSearchProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/search/SpaSearchProvider.kt
@@ -33,6 +33,8 @@
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.common.addUri
import com.android.settingslib.spa.framework.common.getColumns
+import com.android.settingslib.spa.framework.util.SESSION_SEARCH
+import com.android.settingslib.spa.framework.util.createIntent
private const val TAG = "SpaSearchProvider"
@@ -162,20 +164,19 @@
private fun fetchSearchData(entry: SettingsEntry, cursor: MatrixCursor) {
val entryRepository by spaEnvironment.entryRepository
- val browseActivityClass = spaEnvironment.browseActivityClass
// Fetch search data. We can add runtime arguments later if necessary
val searchData = entry.getSearchData() ?: return
- val intent = entry.containerPage()
- .createBrowseIntent(context, browseActivityClass, entry.id)
- ?: Intent()
+ val intent = entry.createIntent(SESSION_SEARCH) ?: Intent()
cursor.newRow()
.add(ColumnEnum.ENTRY_ID.id, entry.id)
.add(ColumnEnum.ENTRY_INTENT_URI.id, intent.toUri(Intent.URI_INTENT_SCHEME))
.add(ColumnEnum.SEARCH_TITLE.id, searchData.title)
.add(ColumnEnum.SEARCH_KEYWORD.id, searchData.keyword)
- .add(ColumnEnum.SEARCH_PATH.id,
- entryRepository.getEntryPathWithTitle(entry.id, searchData.title))
+ .add(
+ ColumnEnum.SEARCH_PATH.id,
+ entryRepository.getEntryPathWithTitle(entry.id, searchData.title)
+ )
}
private fun fetchStatusData(entry: SettingsEntry, cursor: MatrixCursor) {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SettingsSliceDataRepository.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SettingsSliceDataRepository.kt
index 14855a8..7a4750d 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SettingsSliceDataRepository.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SettingsSliceDataRepository.kt
@@ -20,6 +20,7 @@
import android.util.Log
import com.android.settingslib.spa.framework.common.EntrySliceData
import com.android.settingslib.spa.framework.common.SettingsEntryRepository
+import com.android.settingslib.spa.framework.util.getEntryId
private const val TAG = "SliceDataRepository"
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SliceUtil.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SliceUtil.kt
index ff143ed..f362890 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SliceUtil.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SliceUtil.kt
@@ -24,9 +24,15 @@
import android.content.Intent
import android.net.Uri
import android.os.Bundle
-import com.android.settingslib.spa.framework.BrowseActivity.Companion.KEY_DESTINATION
-import com.android.settingslib.spa.framework.BrowseActivity.Companion.KEY_HIGHLIGHT_ENTRY
+import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
+import com.android.settingslib.spa.framework.util.KEY_DESTINATION
+import com.android.settingslib.spa.framework.util.KEY_HIGHLIGHT_ENTRY
+import com.android.settingslib.spa.framework.util.SESSION_SLICE
+import com.android.settingslib.spa.framework.util.SPA_INTENT_RESERVED_KEYS
+import com.android.settingslib.spa.framework.util.appendSpaParams
+import com.android.settingslib.spa.framework.util.getDestination
+import com.android.settingslib.spa.framework.util.getEntryId
// Defines SliceUri, which contains special query parameters:
// -- KEY_DESTINATION: The route that this slice is navigated to.
@@ -35,11 +41,6 @@
// Use {entryId, runtimeParams} as the unique Id of this Slice.
typealias SliceUri = Uri
-val RESERVED_KEYS = listOf(
- KEY_DESTINATION,
- KEY_HIGHLIGHT_ENTRY
-)
-
fun SliceUri.getEntryId(): String? {
return getQueryParameter(KEY_HIGHLIGHT_ENTRY)
}
@@ -51,7 +52,7 @@
fun SliceUri.getRuntimeArguments(): Bundle {
val params = Bundle()
for (queryName in queryParameterNames) {
- if (RESERVED_KEYS.contains(queryName)) continue
+ if (SPA_INTENT_RESERVED_KEYS.contains(queryName)) continue
params.putString(queryName, getQueryParameter(queryName))
}
return params
@@ -63,12 +64,12 @@
return "${entryId}_$params"
}
-fun Uri.Builder.appendSliceParams(
- route: String? = null,
+fun Uri.Builder.appendSpaParams(
+ destination: String? = null,
entryId: String? = null,
runtimeArguments: Bundle? = null
): Uri.Builder {
- if (route != null) appendQueryParameter(KEY_DESTINATION, route)
+ if (destination != null) appendQueryParameter(KEY_DESTINATION, destination)
if (entryId != null) appendQueryParameter(KEY_HIGHLIGHT_ENTRY, entryId)
if (runtimeArguments != null) {
for (key in runtimeArguments.keySet()) {
@@ -78,6 +79,20 @@
return this
}
+fun Uri.Builder.fromEntry(
+ entry: SettingsEntry,
+ authority: String?,
+ runtimeArguments: Bundle? = null
+): Uri.Builder {
+ if (authority == null) return this
+ val sp = entry.containerPage()
+ return scheme("content").authority(authority).appendSpaParams(
+ destination = sp.buildRoute(),
+ entryId = entry.id,
+ runtimeArguments = runtimeArguments
+ )
+}
+
fun SliceUri.createBroadcastPendingIntent(): PendingIntent? {
val context = SpaEnvironmentFactory.instance.appContext
val sliceBroadcastClass =
@@ -97,8 +112,8 @@
fun Intent.createBrowsePendingIntent(): PendingIntent? {
val context = SpaEnvironmentFactory.instance.appContext
val browseActivityClass = SpaEnvironmentFactory.instance.browseActivityClass ?: return null
- val destination = getStringExtra(KEY_DESTINATION) ?: return null
- val entryId = getStringExtra(KEY_HIGHLIGHT_ENTRY)
+ val destination = getDestination() ?: return null
+ val entryId = getEntryId()
return createBrowsePendingIntent(context, browseActivityClass, destination, entryId)
}
@@ -109,15 +124,12 @@
entryId: String?
): PendingIntent {
val intent = Intent().setComponent(ComponentName(context, browseActivityClass))
+ .appendSpaParams(destination, entryId, SESSION_SLICE)
.apply {
// Set both extra and data (which is a Uri) in Slice Intent:
// 1) extra is used in SPA navigation framework
// 2) data is used in Slice framework
- putExtra(KEY_DESTINATION, destination)
- if (entryId != null) {
- putExtra(KEY_HIGHLIGHT_ENTRY, entryId)
- }
- data = Uri.Builder().appendSliceParams(destination, entryId).build()
+ data = Uri.Builder().appendSpaParams(destination, entryId).build()
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
@@ -130,7 +142,7 @@
entryId: String
): PendingIntent {
val intent = Intent().setComponent(ComponentName(context, sliceBroadcastClass))
- .apply { data = Uri.Builder().appendSliceParams(entryId = entryId).build() }
+ .apply { data = Uri.Builder().appendSpaParams(entryId = entryId).build() }
return PendingIntent.getBroadcast(
context, 0 /* requestCode */, intent,
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt
index 934b8f5..c0b7464 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt
@@ -23,6 +23,8 @@
import com.android.settingslib.spa.tests.testutils.SppHome
import com.android.settingslib.spa.tests.testutils.SppLayer1
import com.android.settingslib.spa.tests.testutils.SppLayer2
+import com.android.settingslib.spa.tests.testutils.getUniqueEntryId
+import com.android.settingslib.spa.tests.testutils.getUniquePageId
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryTest.kt
index a343f6c..f98963c 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryTest.kt
@@ -20,6 +20,8 @@
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.core.os.bundleOf
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.tests.testutils.getUniqueEntryId
+import com.android.settingslib.spa.tests.testutils.getUniquePageId
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageTest.kt
index 15c2db50..1f5de2d 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageTest.kt
@@ -22,8 +22,8 @@
import androidx.navigation.navArgument
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settingslib.spa.tests.testutils.BlankActivity
import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest
+import com.android.settingslib.spa.tests.testutils.getUniquePageId
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,9 +43,7 @@
assertThat(page.isCreateBy("NULL")).isTrue()
assertThat(page.isCreateBy("Spp")).isFalse()
assertThat(page.hasRuntimeParam()).isFalse()
- assertThat(page.isBrowsable(context, BlankActivity::class.java)).isFalse()
- assertThat(page.createBrowseIntent(context, BlankActivity::class.java)).isNull()
- assertThat(page.createBrowseAdbCommand(context, BlankActivity::class.java)).isNull()
+ assertThat(page.isBrowsable()).isFalse()
}
@Test
@@ -58,11 +56,7 @@
assertThat(page.isCreateBy("NULL")).isFalse()
assertThat(page.isCreateBy("mySpp")).isTrue()
assertThat(page.hasRuntimeParam()).isFalse()
- assertThat(page.isBrowsable(context, BlankActivity::class.java)).isTrue()
- assertThat(page.createBrowseIntent(context, BlankActivity::class.java)).isNotNull()
- assertThat(page.createBrowseAdbCommand(context, BlankActivity::class.java)).contains(
- "-e spaActivityDestination mySpp"
- )
+ assertThat(page.isBrowsable()).isTrue()
}
@Test
@@ -85,11 +79,7 @@
assertThat(page.buildRoute()).isEqualTo("SppWithParam/myStr/10")
assertThat(page.isCreateBy("SppWithParam")).isTrue()
assertThat(page.hasRuntimeParam()).isFalse()
- assertThat(page.isBrowsable(context, BlankActivity::class.java)).isTrue()
- assertThat(page.createBrowseIntent(context, BlankActivity::class.java)).isNotNull()
- assertThat(page.createBrowseAdbCommand(context, BlankActivity::class.java)).contains(
- "-e spaActivityDestination SppWithParam/myStr/10"
- )
+ assertThat(page.isBrowsable()).isTrue()
}
@Test
@@ -114,8 +104,6 @@
assertThat(page.buildRoute()).isEqualTo("SppWithRtParam/myStr/10/rtStr")
assertThat(page.isCreateBy("SppWithRtParam")).isTrue()
assertThat(page.hasRuntimeParam()).isTrue()
- assertThat(page.isBrowsable(context, BlankActivity::class.java)).isFalse()
- assertThat(page.createBrowseIntent(context, BlankActivity::class.java)).isNull()
- assertThat(page.createBrowseAdbCommand(context, BlankActivity::class.java)).isNull()
+ assertThat(page.isBrowsable()).isFalse()
}
}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/SpaIntentTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/SpaIntentTest.kt
new file mode 100644
index 0000000..1854728
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/SpaIntentTest.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.settingslib.spa.framework.util
+
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPage
+import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
+import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SpaIntentTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+ private val spaEnvironment = SpaEnvironmentForTest(context)
+
+ @Before
+ fun setEnvironment() {
+ SpaEnvironmentFactory.reset(spaEnvironment)
+ }
+
+ @Test
+ fun testCreateIntent() {
+ val nullPage = SettingsPage.createNull()
+ Truth.assertThat(nullPage.createIntent()).isNull()
+ Truth.assertThat(SettingsEntryBuilder.createInject(nullPage).build().createIntent())
+ .isNull()
+
+ val page = spaEnvironment.createPage("SppHome")
+ val pageIntent = page.createIntent()
+ Truth.assertThat(pageIntent).isNotNull()
+ Truth.assertThat(pageIntent!!.getDestination()).isEqualTo(page.buildRoute())
+ Truth.assertThat(pageIntent.getEntryId()).isNull()
+ Truth.assertThat(pageIntent.getSessionName()).isNull()
+
+ val entry = SettingsEntryBuilder.createInject(page).build()
+ val entryIntent = entry.createIntent(SESSION_SEARCH)
+ Truth.assertThat(entryIntent).isNotNull()
+ Truth.assertThat(entryIntent!!.getDestination()).isEqualTo(page.buildRoute())
+ Truth.assertThat(entryIntent.getEntryId()).isEqualTo(entry.id)
+ Truth.assertThat(entryIntent.getSessionName()).isEqualTo(SESSION_SEARCH)
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SettingsSliceDataRepositoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SettingsSliceDataRepositoryTest.kt
index 7fc09ff..90e25f9 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SettingsSliceDataRepositoryTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SettingsSliceDataRepositoryTest.kt
@@ -23,11 +23,11 @@
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.framework.common.createSettingsPage
-import com.android.settingslib.spa.framework.common.getUniqueEntryId
import com.android.settingslib.spa.testutils.InstantTaskExecutorRule
import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest
import com.android.settingslib.spa.tests.testutils.SppHome
import com.android.settingslib.spa.tests.testutils.SppLayer2
+import com.android.settingslib.spa.tests.testutils.getUniqueEntryId
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
@@ -50,7 +50,7 @@
// Slice supported
val page = SppLayer2.createSettingsPage()
val entryId = getUniqueEntryId("Layer2Entry1", page)
- val sliceUri = Uri.Builder().appendSliceParams(page.buildRoute(), entryId).build()
+ val sliceUri = Uri.Builder().appendSpaParams(page.buildRoute(), entryId).build()
assertThat(sliceUri.getDestination()).isEqualTo("SppLayer2")
assertThat(sliceUri.getSliceId()).isEqualTo("${entryId}_Bundle[{}]")
val sliceData = sliceDataRepository.getOrBuildSliceData(sliceUri)
@@ -59,7 +59,7 @@
// Slice unsupported
val entryId2 = getUniqueEntryId("Layer2Entry2", page)
- val sliceUri2 = Uri.Builder().appendSliceParams(page.buildRoute(), entryId2).build()
+ val sliceUri2 = Uri.Builder().appendSpaParams(page.buildRoute(), entryId2).build()
assertThat(sliceUri2.getDestination()).isEqualTo("SppLayer2")
assertThat(sliceUri2.getSliceId()).isEqualTo("${entryId2}_Bundle[{}]")
assertThat(sliceDataRepository.getOrBuildSliceData(sliceUri2)).isNull()
@@ -69,7 +69,7 @@
fun getActiveSliceDataTest() {
val page = SppLayer2.createSettingsPage()
val entryId = getUniqueEntryId("Layer2Entry1", page)
- val sliceUri = Uri.Builder().appendSliceParams(page.buildRoute(), entryId).build()
+ val sliceUri = Uri.Builder().appendSpaParams(page.buildRoute(), entryId).build()
// build slice data first
val sliceData = sliceDataRepository.getOrBuildSliceData(sliceUri)
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SliceUtilTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SliceUtilTest.kt
index 16a87f6..d1c4e51 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SliceUtilTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SliceUtilTest.kt
@@ -43,14 +43,14 @@
// valid slice uri
val dest = "myRoute"
val entryId = "myEntry"
- val sliceUriWithoutParams = Uri.Builder().appendSliceParams(dest, entryId).build()
+ val sliceUriWithoutParams = Uri.Builder().appendSpaParams(dest, entryId).build()
assertThat(sliceUriWithoutParams.getEntryId()).isEqualTo(entryId)
assertThat(sliceUriWithoutParams.getDestination()).isEqualTo(dest)
assertThat(sliceUriWithoutParams.getRuntimeArguments().size()).isEqualTo(0)
assertThat(sliceUriWithoutParams.getSliceId()).isEqualTo("${entryId}_Bundle[{}]")
val sliceUriWithParams =
- Uri.Builder().appendSliceParams(dest, entryId, bundleOf("p1" to "v1")).build()
+ Uri.Builder().appendSpaParams(dest, entryId, bundleOf("p1" to "v1")).build()
assertThat(sliceUriWithParams.getEntryId()).isEqualTo(entryId)
assertThat(sliceUriWithParams.getDestination()).isEqualTo(dest)
assertThat(sliceUriWithParams.getRuntimeArguments().size()).isEqualTo(1)
@@ -67,7 +67,7 @@
// Valid Slice Uri
val dest = "myRoute"
val entryId = "myEntry"
- val sliceUriWithoutParams = Uri.Builder().appendSliceParams(dest, entryId).build()
+ val sliceUriWithoutParams = Uri.Builder().appendSpaParams(dest, entryId).build()
val pendingIntent = sliceUriWithoutParams.createBroadcastPendingIntent()
assertThat(pendingIntent).isNotNull()
assertThat(pendingIntent!!.isBroadcast).isTrue()
@@ -87,7 +87,7 @@
// Valid Slice Uri
val dest = "myRoute"
val entryId = "myEntry"
- val sliceUri = Uri.Builder().appendSliceParams(dest, entryId).build()
+ val sliceUri = Uri.Builder().appendSpaParams(dest, entryId).build()
val pendingIntent = sliceUri.createBrowsePendingIntent()
assertThat(pendingIntent).isNotNull()
assertThat(pendingIntent!!.isActivity).isTrue()
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/UniqueIdHelper.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/UniqueIdHelper.kt
similarity index 89%
rename from packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/UniqueIdHelper.kt
rename to packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/UniqueIdHelper.kt
index 93f9afe..7e51fea 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/UniqueIdHelper.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/UniqueIdHelper.kt
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.framework.common
+package com.android.settingslib.spa.tests.testutils
import android.os.Bundle
import androidx.navigation.NamedNavArgument
+import com.android.settingslib.spa.framework.common.SettingsPage
+import com.android.settingslib.spa.framework.common.toHashId
import com.android.settingslib.spa.framework.util.normalize
fun getUniquePageId(
diff --git a/services/api/current.txt b/services/api/current.txt
index 35a4dcb..834ed2f 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -76,6 +76,7 @@
method @Nullable public String getSdkLibraryName();
method @NonNull public java.util.List<com.android.server.pm.pkg.AndroidPackageSplit> getSplits();
method @Nullable public String getStaticSharedLibraryName();
+ method @NonNull public java.util.UUID getStorageUuid();
method public int getTargetSdkVersion();
method public boolean isDebuggable();
method public boolean isIsolatedSplitLoading();
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 05cd67f..c5cb08d 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -105,7 +105,6 @@
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
-import android.sysprop.DisplayProperties;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.EventLog;
@@ -451,8 +450,6 @@
}
};
- private final boolean mAllowNonNativeRefreshRateOverride;
-
private final BrightnessSynchronizer mBrightnessSynchronizer;
/**
@@ -506,7 +503,6 @@
ColorSpace[] colorSpaces = SurfaceControl.getCompositionColorSpaces();
mWideColorSpace = colorSpaces[1];
mOverlayProperties = SurfaceControl.getOverlaySupport();
- mAllowNonNativeRefreshRateOverride = mInjector.getAllowNonNativeRefreshRateOverride();
mSystemReady = false;
}
@@ -930,24 +926,20 @@
}
}
- if (mAllowNonNativeRefreshRateOverride) {
- overriddenInfo.refreshRateOverride = frameRateHz;
- if (!CompatChanges.isChangeEnabled(DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE,
- callingUid)) {
- overriddenInfo.supportedModes = Arrays.copyOf(info.supportedModes,
- info.supportedModes.length + 1);
- overriddenInfo.supportedModes[overriddenInfo.supportedModes.length - 1] =
- new Display.Mode(Display.DISPLAY_MODE_ID_FOR_FRAME_RATE_OVERRIDE,
- currentMode.getPhysicalWidth(), currentMode.getPhysicalHeight(),
- overriddenInfo.refreshRateOverride);
- overriddenInfo.modeId =
- overriddenInfo.supportedModes[overriddenInfo.supportedModes.length - 1]
- .getModeId();
- }
- return overriddenInfo;
+ overriddenInfo.refreshRateOverride = frameRateHz;
+ if (!CompatChanges.isChangeEnabled(DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE,
+ callingUid)) {
+ overriddenInfo.supportedModes = Arrays.copyOf(info.supportedModes,
+ info.supportedModes.length + 1);
+ overriddenInfo.supportedModes[overriddenInfo.supportedModes.length - 1] =
+ new Display.Mode(Display.DISPLAY_MODE_ID_FOR_FRAME_RATE_OVERRIDE,
+ currentMode.getPhysicalWidth(), currentMode.getPhysicalHeight(),
+ overriddenInfo.refreshRateOverride);
+ overriddenInfo.modeId =
+ overriddenInfo.supportedModes[overriddenInfo.supportedModes.length - 1]
+ .getModeId();
}
-
- return info;
+ return overriddenInfo;
}
private DisplayInfo getDisplayInfoInternal(int displayId, int callingUid) {
@@ -2602,11 +2594,6 @@
long getDefaultDisplayDelayTimeout() {
return WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT;
}
-
- boolean getAllowNonNativeRefreshRateOverride() {
- return DisplayProperties
- .debug_allow_non_native_refresh_rate_override().orElse(true);
- }
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 3c72019..283640d 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2259,26 +2259,20 @@
incrementalStorages.add(storage);
}
- // Enabling fs-verity is a blocking operation. To reduce the impact to the install time,
- // run in a background thread.
- new Thread("fsverity-setup") {
- @Override public void run() {
- try {
- if (!VerityUtils.hasFsverity(pkg.getBaseApkPath())) {
- VerityUtils.setUpFsverity(pkg.getBaseApkPath(), (byte[]) null);
- }
- for (String path : pkg.getSplitCodePaths()) {
- if (!VerityUtils.hasFsverity(path)) {
- VerityUtils.setUpFsverity(path, (byte[]) null);
- }
- }
- } catch (IOException e) {
- // There's nothing we can do if the setup failed. Since fs-verity is
- // optional, just ignore the error for now.
- Slog.e(TAG, "Failed to fully enable fs-verity to " + packageName);
+ try {
+ if (!VerityUtils.hasFsverity(pkg.getBaseApkPath())) {
+ VerityUtils.setUpFsverity(pkg.getBaseApkPath(), (byte[]) null);
+ }
+ for (String path : pkg.getSplitCodePaths()) {
+ if (!VerityUtils.hasFsverity(path)) {
+ VerityUtils.setUpFsverity(path, (byte[]) null);
}
}
- }.start();
+ } catch (IOException e) {
+ // There's nothing we can do if the setup failed. Since fs-verity is
+ // optional, just ignore the error for now.
+ Slog.e(TAG, "Failed to fully enable fs-verity to " + packageName);
+ }
// Hardcode previousAppId to 0 to disable any data migration (http://b/221088088)
mAppDataHelper.prepareAppDataPostCommitLIF(pkg, 0);
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index b1e9141..ba36ab7 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -1335,6 +1335,11 @@
}
@Override
+ public UUID getStorageUuid() {
+ return mStorageUuid;
+ }
+
+ @Override
public int getTargetSandboxVersion() {
return targetSandboxVersion;
}
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
index e3dad45..84907a5 100644
--- a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -34,6 +34,7 @@
import android.content.pm.ServiceInfo;
import android.content.pm.SigningDetails;
import android.os.Bundle;
+import android.os.storage.StorageManager;
import android.processor.immutability.Immutable;
import android.util.ArraySet;
import android.util.Pair;
@@ -58,6 +59,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
/**
* The representation of an application on disk, as parsed from its split APKs' manifests.
@@ -111,6 +113,13 @@
String getStaticSharedLibraryName();
/**
+ * @return The {@link UUID} for use with {@link StorageManager} APIs identifying where this
+ * package was installed.
+ */
+ @NonNull
+ UUID getStorageUuid();
+
+ /**
* @see ApplicationInfo#targetSdkVersion
* @see R.styleable#AndroidManifestUsesSdk_targetSdkVersion
*/
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index f74956b..5d08461 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1559,8 +1559,9 @@
try {
mReply.sendResult(null);
} catch (RemoteException e) {
- Binder.restoreCallingIdentity(ident);
Slog.d(TAG, "failed to send callback!", e);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
t.traceEnd();
mReply = null;
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index ee6196d..7bf9a9e 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -48,11 +48,16 @@
import com.android.server.testutils.whenever
import java.security.KeyPairGenerator
import java.security.PublicKey
+import java.util.UUID
import kotlin.contracts.ExperimentalContracts
@ExperimentalContracts
class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, PackageImpl::class) {
+ companion object {
+ private val TEST_UUID = UUID.fromString("57554103-df3e-4475-ae7a-8feba49353ac")
+ }
+
override val defaultImpl = PackageImpl.forTesting("com.example.test")
override val creator = PackageImpl.CREATOR
@@ -88,8 +93,6 @@
"getLongVersionCode",
// Tested through constructor
"getManifestPackageName",
- // Utility methods
- "getStorageUuid",
// Removal not tested, irrelevant for parcelling concerns
"removeUsesOptionalLibrary",
"clearAdoptPermissions",
@@ -101,6 +104,7 @@
// Tested manually
"getMimeGroups",
"getRequestedPermissions",
+ "getStorageUuid",
// Tested through asSplit
"asSplit",
"getSplits",
@@ -256,7 +260,7 @@
)
override fun extraParams() = listOf(
- getter(AndroidPackage::getVolumeUuid, "57554103-df3e-4475-ae7a-8feba49353ac"),
+ getter(AndroidPackage::getVolumeUuid, TEST_UUID.toString()),
getter(AndroidPackage::isProfileable, true),
getter(PackageImpl::getVersionCode, 3),
getter(PackageImpl::getVersionCodeMajor, 9),
@@ -617,6 +621,8 @@
expect.that(after.usesStaticLibrariesCertDigests!!.size).isEqualTo(1)
expect.that(after.usesStaticLibrariesCertDigests!![0]).asList()
.containsExactly("testCertDigest2")
+
+ expect.that(after.storageUuid).isEqualTo(TEST_UUID)
}
private fun testKey() = KeyPairGenerator.getInstance("RSA")
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java
index 5c3d695..01674bb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.timeout;
@@ -107,12 +108,16 @@
@Mock
private BackgroundDexOptJobService mJobServiceForIdle;
- private final JobParameters mJobParametersForPostBoot = new JobParameters(null,
- BackgroundDexOptService.JOB_POST_BOOT_UPDATE, null, null, null,
- 0, false, false, null, null, null);
- private final JobParameters mJobParametersForIdle = new JobParameters(null,
- BackgroundDexOptService.JOB_IDLE_OPTIMIZE, null, null, null,
- 0, false, false, null, null, null);
+ private final JobParameters mJobParametersForPostBoot =
+ createJobParameters(BackgroundDexOptService.JOB_POST_BOOT_UPDATE);
+ private final JobParameters mJobParametersForIdle =
+ createJobParameters(BackgroundDexOptService.JOB_IDLE_OPTIMIZE);
+
+ private static JobParameters createJobParameters(int jobId) {
+ JobParameters params = mock(JobParameters.class);
+ when(params.getJobId()).thenReturn(jobId);
+ return params;
+ }
private BackgroundDexOptService mService;
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 9853b7a..ce35626 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -171,22 +171,6 @@
private final DisplayManagerService.Injector mBasicInjector = new BasicInjector();
- private final DisplayManagerService.Injector mAllowNonNativeRefreshRateOverrideInjector =
- new BasicInjector() {
- @Override
- boolean getAllowNonNativeRefreshRateOverride() {
- return true;
- }
- };
-
- private final DisplayManagerService.Injector mDenyNonNativeRefreshRateOverrideInjector =
- new BasicInjector() {
- @Override
- boolean getAllowNonNativeRefreshRateOverride() {
- return false;
- }
- };
-
@Mock InputManagerInternal mMockInputManagerInternal;
@Mock VirtualDeviceManagerInternal mMockVirtualDeviceManagerInternal;
@Mock IVirtualDisplayCallback.Stub mMockAppToken;
@@ -1113,13 +1097,32 @@
}
/**
- * Tests that the frame rate override is updated accordingly to the
- * allowNonNativeRefreshRateOverride policy.
+ * Tests that the frame rate override is returning the correct value from
+ * DisplayInfo#getRefreshRate
*/
@Test
public void testDisplayInfoNonNativeFrameRateOverride() throws Exception {
- testDisplayInfoNonNativeFrameRateOverride(mDenyNonNativeRefreshRateOverrideInjector);
- testDisplayInfoNonNativeFrameRateOverride(mAllowNonNativeRefreshRateOverrideInjector);
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ registerDefaultDisplays(displayManager);
+ displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
+
+ FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager,
+ new float[]{60f});
+ int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService,
+ displayDevice);
+ DisplayInfo displayInfo = displayManagerBinderService.getDisplayInfo(displayId);
+ assertEquals(60f, displayInfo.getRefreshRate(), 0.01f);
+
+ updateFrameRateOverride(displayManager, displayDevice,
+ new DisplayEventReceiver.FrameRateOverride[]{
+ new DisplayEventReceiver.FrameRateOverride(
+ Process.myUid(), 20f)
+ });
+ displayInfo = displayManagerBinderService.getDisplayInfo(displayId);
+ assertEquals(20f, displayInfo.getRefreshRate(), 0.01f);
}
/**
@@ -1147,10 +1150,7 @@
@Test
@DisableCompatChanges({DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE})
public void testDisplayInfoNonNativeFrameRateOverrideModeCompat() throws Exception {
- testDisplayInfoNonNativeFrameRateOverrideMode(mDenyNonNativeRefreshRateOverrideInjector,
- /*compatChangeEnabled*/ false);
- testDisplayInfoNonNativeFrameRateOverrideMode(mAllowNonNativeRefreshRateOverrideInjector,
- /*compatChangeEnabled*/ false);
+ testDisplayInfoNonNativeFrameRateOverrideMode(/*compatChangeEnabled*/ false);
}
/**
@@ -1159,10 +1159,7 @@
@Test
@EnableCompatChanges({DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE})
public void testDisplayInfoNonNativeFrameRateOverrideMode() throws Exception {
- testDisplayInfoNonNativeFrameRateOverrideMode(mDenyNonNativeRefreshRateOverrideInjector,
- /*compatChangeEnabled*/ true);
- testDisplayInfoNonNativeFrameRateOverrideMode(mAllowNonNativeRefreshRateOverrideInjector,
- /*compatChangeEnabled*/ true);
+ testDisplayInfoNonNativeFrameRateOverrideMode(/*compatChangeEnabled*/ true);
}
/**
@@ -1385,10 +1382,9 @@
assertEquals(expectedMode, displayInfo.getMode());
}
- private void testDisplayInfoNonNativeFrameRateOverrideMode(
- DisplayManagerService.Injector injector, boolean compatChangeEnabled) {
+ private void testDisplayInfoNonNativeFrameRateOverrideMode(boolean compatChangeEnabled) {
DisplayManagerService displayManager =
- new DisplayManagerService(mContext, injector);
+ new DisplayManagerService(mContext, mBasicInjector);
DisplayManagerService.BinderService displayManagerBinderService =
displayManager.new BinderService();
registerDefaultDisplays(displayManager);
@@ -1410,40 +1406,12 @@
Display.Mode expectedMode;
if (compatChangeEnabled) {
expectedMode = new Display.Mode(1, 100, 200, 60f);
- } else if (injector.getAllowNonNativeRefreshRateOverride()) {
- expectedMode = new Display.Mode(255, 100, 200, 20f);
} else {
- expectedMode = new Display.Mode(1, 100, 200, 60f);
+ expectedMode = new Display.Mode(255, 100, 200, 20f);
}
assertEquals(expectedMode, displayInfo.getMode());
}
- private void testDisplayInfoNonNativeFrameRateOverride(
- DisplayManagerService.Injector injector) {
- DisplayManagerService displayManager =
- new DisplayManagerService(mContext, injector);
- DisplayManagerService.BinderService displayManagerBinderService =
- displayManager.new BinderService();
- registerDefaultDisplays(displayManager);
- displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
-
- FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager,
- new float[]{60f});
- int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService,
- displayDevice);
- DisplayInfo displayInfo = displayManagerBinderService.getDisplayInfo(displayId);
- assertEquals(60f, displayInfo.getRefreshRate(), 0.01f);
-
- updateFrameRateOverride(displayManager, displayDevice,
- new DisplayEventReceiver.FrameRateOverride[]{
- new DisplayEventReceiver.FrameRateOverride(
- Process.myUid(), 20f)
- });
- displayInfo = displayManagerBinderService.getDisplayInfo(displayId);
- float expectedRefreshRate = injector.getAllowNonNativeRefreshRateOverride() ? 20f : 60f;
- assertEquals(expectedRefreshRate, displayInfo.getRefreshRate(), 0.01f);
- }
-
private int getDisplayIdForDisplayDevice(
DisplayManagerService displayManager,
DisplayManagerService.BinderService displayManagerBinderService,
diff --git a/tools/processors/immutability/src/android/processor/immutability/ImmutabilityProcessor.kt b/tools/processors/immutability/src/android/processor/immutability/ImmutabilityProcessor.kt
index f29d9b2..c6f6d45 100644
--- a/tools/processors/immutability/src/android/processor/immutability/ImmutabilityProcessor.kt
+++ b/tools/processors/immutability/src/android/processor/immutability/ImmutabilityProcessor.kt
@@ -54,6 +54,7 @@
"java.lang.Short",
"java.lang.String",
"java.lang.Void",
+ "java.util.UUID",
"android.os.Parcelable.Creator",
)