Merge "Signal dream end to overlay in finish()."
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 2533a0f..d7163d8 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -4805,10 +4805,14 @@
}
final ArraySet<UserPackage> triggerPackages = new ArraySet<>();
final IntArray wakeupUids = new IntArray();
+ final SparseIntArray countsPerUid = new SparseIntArray();
+ final SparseIntArray wakeupCountsPerUid = new SparseIntArray();
for (int i = 0; i < triggerList.size(); i++) {
final Alarm a = triggerList.get(i);
+ increment(countsPerUid, a.uid);
if (a.wakeup) {
wakeupUids.add(a.uid);
+ increment(wakeupCountsPerUid, a.uid);
}
if (mConstants.USE_TARE_POLICY == EconomyManager.ENABLED_MODE_ON) {
if (!isExemptFromTare(a)) {
@@ -4835,7 +4839,8 @@
}
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
- MetricsHelper.pushAlarmBatchDelivered(triggerList.size(), wakeUps);
+ logAlarmBatchDelivered(
+ triggerList.size(), wakeUps, countsPerUid, wakeupCountsPerUid);
}
}
@@ -4850,6 +4855,32 @@
}
}
+ private static void increment(SparseIntArray array, int key) {
+ final int index = array.indexOfKey(key);
+ if (index >= 0) {
+ array.setValueAt(index, array.valueAt(index) + 1);
+ } else {
+ array.put(key, 1);
+ }
+ }
+
+ private void logAlarmBatchDelivered(
+ int alarms,
+ int wakeups,
+ SparseIntArray countsPerUid,
+ SparseIntArray wakeupCountsPerUid) {
+ final int[] uids = new int[countsPerUid.size()];
+ final int[] countsArray = new int[countsPerUid.size()];
+ final int[] wakeupCountsArray = new int[countsPerUid.size()];
+ for (int i = 0; i < countsPerUid.size(); i++) {
+ uids[i] = countsPerUid.keyAt(i);
+ countsArray[i] = countsPerUid.valueAt(i);
+ wakeupCountsArray[i] = wakeupCountsPerUid.get(uids[i], 0);
+ }
+ MetricsHelper.pushAlarmBatchDelivered(
+ alarms, wakeups, uids, countsArray, wakeupCountsArray);
+ }
+
/**
* Attribute blame for a WakeLock.
*
@@ -5766,12 +5797,7 @@
}
private void incrementAlarmCount(int uid) {
- final int uidIndex = mAlarmsPerUid.indexOfKey(uid);
- if (uidIndex >= 0) {
- mAlarmsPerUid.setValueAt(uidIndex, mAlarmsPerUid.valueAt(uidIndex) + 1);
- } else {
- mAlarmsPerUid.put(uid, 1);
- }
+ increment(mAlarmsPerUid, uid);
}
/**
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java b/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java
index 28acb45..eb1848d 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java
@@ -117,10 +117,14 @@
ActivityManager.processStateAmToProto(callerProcState));
}
- static void pushAlarmBatchDelivered(int numAlarms, int wakeups) {
+ static void pushAlarmBatchDelivered(
+ int numAlarms, int wakeups, int[] uids, int[] alarmsPerUid, int[] wakeupAlarmsPerUid) {
FrameworkStatsLog.write(
FrameworkStatsLog.ALARM_BATCH_DELIVERED,
numAlarms,
- wakeups);
+ wakeups,
+ uids,
+ alarmsPerUid,
+ wakeupAlarmsPerUid);
}
}
diff --git a/core/api/current.txt b/core/api/current.txt
index e87487a..2cfc257 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -20341,7 +20341,7 @@
method @NonNull public android.location.GnssClock getClock();
method @NonNull public java.util.Collection<android.location.GnssAutomaticGainControl> getGnssAutomaticGainControls();
method @NonNull public java.util.Collection<android.location.GnssMeasurement> getMeasurements();
- method public boolean hasFullTracking();
+ method public boolean hasIsFullTracking();
method public boolean isFullTracking();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssMeasurementsEvent> CREATOR;
@@ -20351,10 +20351,10 @@
ctor public GnssMeasurementsEvent.Builder();
ctor public GnssMeasurementsEvent.Builder(@NonNull android.location.GnssMeasurementsEvent);
method @NonNull public android.location.GnssMeasurementsEvent build();
- method @NonNull public android.location.GnssMeasurementsEvent.Builder clearFullTracking();
+ method @NonNull public android.location.GnssMeasurementsEvent.Builder clearIsFullTracking();
method @NonNull public android.location.GnssMeasurementsEvent.Builder setClock(@NonNull android.location.GnssClock);
- method @NonNull public android.location.GnssMeasurementsEvent.Builder setFullTracking(boolean);
method @NonNull public android.location.GnssMeasurementsEvent.Builder setGnssAutomaticGainControls(@NonNull java.util.Collection<android.location.GnssAutomaticGainControl>);
+ method @NonNull public android.location.GnssMeasurementsEvent.Builder setIsFullTracking(boolean);
method @NonNull public android.location.GnssMeasurementsEvent.Builder setMeasurements(@NonNull java.util.Collection<android.location.GnssMeasurement>);
}
@@ -21369,7 +21369,8 @@
field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioPlaybackConfiguration> CREATOR;
}
- public final class AudioPresentation {
+ public final class AudioPresentation implements android.os.Parcelable {
+ method public int describeContents();
method public java.util.Map<java.util.Locale,java.lang.String> getLabels();
method public java.util.Locale getLocale();
method public int getMasteringIndication();
@@ -21378,6 +21379,7 @@
method public boolean hasAudioDescription();
method public boolean hasDialogueEnhancement();
method public boolean hasSpokenSubtitles();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int CONTENT_COMMENTARY = 5; // 0x5
field public static final int CONTENT_DIALOG = 4; // 0x4
field public static final int CONTENT_EMERGENCY = 6; // 0x6
@@ -21387,11 +21389,14 @@
field public static final int CONTENT_UNKNOWN = -1; // 0xffffffff
field public static final int CONTENT_VISUALLY_IMPAIRED = 2; // 0x2
field public static final int CONTENT_VOICEOVER = 7; // 0x7
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioPresentation> CREATOR;
field public static final int MASTERED_FOR_3D = 3; // 0x3
field public static final int MASTERED_FOR_HEADPHONE = 4; // 0x4
field public static final int MASTERED_FOR_STEREO = 1; // 0x1
field public static final int MASTERED_FOR_SURROUND = 2; // 0x2
field public static final int MASTERING_NOT_INDICATED = 0; // 0x0
+ field public static final int PRESENTATION_ID_UNKNOWN = -1; // 0xffffffff
+ field public static final int PROGRAM_ID_UNKNOWN = -1; // 0xffffffff
}
public static final class AudioPresentation.Builder {
@@ -27024,6 +27029,7 @@
method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(@NonNull String, @NonNull String);
method @Nullable public abstract android.media.tv.TvInputService.Session onCreateSession(@NonNull String);
method @Nullable public android.media.tv.TvInputService.Session onCreateSession(@NonNull String, @NonNull String);
+ method @Nullable public android.media.tv.TvInputService.Session onCreateSession(@NonNull String, @NonNull String, @NonNull android.content.AttributionSource);
field public static final int PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND = 100; // 0x64
field public static final int PRIORITY_HINT_USE_CASE_TYPE_LIVE = 400; // 0x190
field public static final int PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK = 300; // 0x12c
@@ -27062,6 +27068,8 @@
method public void layoutSurface(int, int, int, int);
method public void notifyAdResponse(@NonNull android.media.tv.AdResponse);
method public void notifyAitInfoUpdated(@NonNull android.media.tv.AitInfo);
+ method public void notifyAudioPresentationChanged(@NonNull java.util.List<android.media.AudioPresentation>);
+ method public void notifyAudioPresentationSelected(int, int);
method public void notifyBroadcastInfoResponse(@NonNull android.media.tv.BroadcastInfoResponse);
method public void notifyChannelRetuned(android.net.Uri);
method public void notifyContentAllowed();
@@ -27085,6 +27093,7 @@
method public void onRemoveBroadcastInfo(int);
method public void onRequestAd(@NonNull android.media.tv.AdRequest);
method public void onRequestBroadcastInfo(@NonNull android.media.tv.BroadcastInfoRequest);
+ method public boolean onSelectAudioPresentation(int, int);
method public boolean onSelectTrack(int, @Nullable String);
method public abstract void onSetCaptionEnabled(boolean);
method public void onSetInteractiveAppNotificationEnabled(boolean);
@@ -27214,10 +27223,13 @@
ctor public TvView(android.content.Context, android.util.AttributeSet);
ctor public TvView(android.content.Context, android.util.AttributeSet, int);
method public boolean dispatchUnhandledInputEvent(android.view.InputEvent);
+ method @NonNull public java.util.List<android.media.AudioPresentation> getAudioPresentations();
method public String getSelectedTrack(int);
method public java.util.List<android.media.tv.TvTrackInfo> getTracks(int);
method public boolean onUnhandledInputEvent(android.view.InputEvent);
+ method public void overrideTvAppAttributionSource(@NonNull android.content.AttributionSource);
method public void reset();
+ method public void selectAudioPresentation(int, int);
method public void selectTrack(int, String);
method public void sendAppPrivateCommand(@NonNull String, android.os.Bundle);
method public void setCallback(@Nullable android.media.tv.TvView.TvInputCallback);
@@ -27250,6 +27262,8 @@
public abstract static class TvView.TvInputCallback {
ctor public TvView.TvInputCallback();
method public void onAitInfoUpdated(@NonNull String, @NonNull android.media.tv.AitInfo);
+ method public void onAudioPresentationSelected(@NonNull String, int, int);
+ method public void onAudioPresentationsChanged(@NonNull String, @NonNull java.util.List<android.media.AudioPresentation>);
method public void onChannelRetuned(String, android.net.Uri);
method public void onConnectionFailed(String);
method public void onContentAllowed(String);
@@ -41625,9 +41639,7 @@
}
public final class CallEndpointException extends java.lang.RuntimeException implements android.os.Parcelable {
- ctor public CallEndpointException(@Nullable String);
ctor public CallEndpointException(@Nullable String, int);
- ctor public CallEndpointException(@Nullable String, int, @Nullable Throwable);
method public int describeContents();
method public int getCode();
method public void writeToParcel(@NonNull android.os.Parcel, int);
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 577c8a3..42aa608 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -902,8 +902,8 @@
* <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code VIDEO_CALL}</td> <td colspan="3" id="rb"></td> <td>Preview with video call</td> </tr>
* <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code PREVIEW_VIDEO_STILL}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td>MultI-purpose stream with JPEG or YUV still capture</td> </tr>
* <tr> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td>YUV and JPEG concurrent still image capture (for testing)</td> </tr>
- * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code VIDEO_RECORD}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, video record and JPEG or YUV video snapshot</td> </tr>
- * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, in-application image processing, and JPEG or YUV still image capture</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code VIDEO_RECORD}</td> <td>{@code JPEG}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, video record and JPEG video snapshot</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, in-application image processing, and JPEG still image capture</td> </tr>
* </table><br>
* </p>
*
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 3fc44f8..86c453b 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -1225,14 +1225,6 @@
new StreamCombinationTemplate(new StreamTemplate [] {
new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
STREAM_USE_CASE_PREVIEW),
- new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD,
- STREAM_USE_CASE_RECORD),
- new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
- STREAM_USE_CASE_STILL_CAPTURE)},
- "Preview, video record and YUV video snapshot"),
- new StreamCombinationTemplate(new StreamTemplate [] {
- new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
- STREAM_USE_CASE_PREVIEW),
new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
STREAM_USE_CASE_RECORD),
new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD,
@@ -1241,27 +1233,11 @@
new StreamCombinationTemplate(new StreamTemplate [] {
new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
STREAM_USE_CASE_PREVIEW),
- new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
- STREAM_USE_CASE_RECORD),
- new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
- STREAM_USE_CASE_STILL_CAPTURE)},
- "Preview, in-application video processing and YUV video snapshot"),
- new StreamCombinationTemplate(new StreamTemplate [] {
- new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
- STREAM_USE_CASE_PREVIEW),
new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
STREAM_USE_CASE_PREVIEW),
new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
STREAM_USE_CASE_STILL_CAPTURE)},
"Preview, in-application image processing, and JPEG still image capture"),
- new StreamCombinationTemplate(new StreamTemplate [] {
- new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
- STREAM_USE_CASE_PREVIEW),
- new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
- STREAM_USE_CASE_PREVIEW),
- new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
- STREAM_USE_CASE_STILL_CAPTURE)},
- "Preview, in-application image processing, and YUV still image capture"),
};
private static StreamCombinationTemplate sCroppedRawStreamUseCaseCombinations[] = {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7b53843..d68e085 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -25829,10 +25829,6 @@
* @param selected true if the view must be selected, false otherwise
*/
public void setSelected(boolean selected) {
- setSelected(selected, true);
- }
-
- void setSelected(boolean selected, boolean sendAccessibilityEvent) {
//noinspection DoubleNegation
if (((mPrivateFlags & PFLAG_SELECTED) != 0) != selected) {
mPrivateFlags = (mPrivateFlags & ~PFLAG_SELECTED) | (selected ? PFLAG_SELECTED : 0);
@@ -25840,13 +25836,11 @@
invalidate(true);
refreshDrawableState();
dispatchSetSelected(selected);
- if (sendAccessibilityEvent) {
- if (selected) {
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
- } else {
- notifyViewAccessibilityStateChangedIfNeeded(
- AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
- }
+ if (selected) {
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
+ } else {
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 9f5015c..0e4ac01 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4631,7 +4631,7 @@
final View[] children = mChildren;
final int count = mChildrenCount;
for (int i = 0; i < count; i++) {
- children[i].setSelected(selected, false);
+ children[i].setSelected(selected);
}
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index d9cb72a..cee8c1a 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -178,7 +178,14 @@
* GWP-ASan is activated unconditionally (but still, only a small subset of
* allocations is protected).
*/
- public static final int GWP_ASAN_LEVEL_ALWAYS = 1 << 22;
+ public static final int GWP_ASAN_LEVEL_ALWAYS = 2 << 21;
+
+ /**
+ * GWP-ASan's `gwpAsanMode` manifest flag was unspecified. Currently, this
+ * means GWP_ASAN_LEVEL_LOTTERY for system apps, and GWP_ASAN_LEVEL_NONE for
+ * non-system apps.
+ */
+ public static final int GWP_ASAN_LEVEL_DEFAULT = 3 << 21;
/** Enable automatic zero-initialization of native heap memory allocations. */
public static final int NATIVE_HEAP_ZERO_INIT_ENABLED = 1 << 23;
@@ -1356,15 +1363,13 @@
? GWP_ASAN_LEVEL_ALWAYS
: GWP_ASAN_LEVEL_NEVER;
}
- // If the app does not specify gwpAsanMode, the default behavior is lottery among the
- // system apps, and disabled for user apps, unless overwritten by the compat feature.
if (isCompatChangeEnabled(GWP_ASAN, info, platformCompat, 0)) {
return GWP_ASAN_LEVEL_ALWAYS;
}
if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
return GWP_ASAN_LEVEL_LOTTERY;
}
- return GWP_ASAN_LEVEL_NEVER;
+ return GWP_ASAN_LEVEL_DEFAULT;
}
private static boolean enableNativeHeapZeroInit(
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index eca85ff..d8dffcd 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -266,7 +266,6 @@
"av-types-aidl-cpp",
"android.hardware.camera.device@3.2",
"libandroid_net",
- "libandroidicu",
"libbattery",
"libnetdutils",
"libmemtrack",
@@ -287,6 +286,7 @@
"libPlatformProperties",
"libsensor",
"libinput",
+ "libicu",
"libcamera_client",
"libcamera_metadata",
"libprocinfo",
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 664e964..6a92581 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -353,6 +353,7 @@
GWP_ASAN_LEVEL_NEVER = 0 << 21,
GWP_ASAN_LEVEL_LOTTERY = 1 << 21,
GWP_ASAN_LEVEL_ALWAYS = 2 << 21,
+ GWP_ASAN_LEVEL_DEFAULT = 3 << 21,
NATIVE_HEAP_ZERO_INIT_ENABLED = 1 << 23,
PROFILEABLE = 1 << 24,
};
@@ -1932,6 +1933,13 @@
gwp_asan_options.program_name = nice_name_ptr ?: process_name;
switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) {
default:
+ case RuntimeFlags::GWP_ASAN_LEVEL_DEFAULT:
+ // TODO(b/247012630): Switch this to Action::TURN_ON_FOR_APP_SAMPLED_NON_CRASHING once
+ // performance and syshealth testing is completed, making the default for non-system
+ // apps that don't specify a `gwpAsanMode` in their manifest to be sampled-recoverable.
+ gwp_asan_options.desire = Action::DONT_TURN_ON_UNLESS_OVERRIDDEN;
+ android_mallopt(M_INITIALIZE_GWP_ASAN, &gwp_asan_options, sizeof(gwp_asan_options));
+ break;
case RuntimeFlags::GWP_ASAN_LEVEL_NEVER:
gwp_asan_options.desire = Action::DONT_TURN_ON_UNLESS_OVERRIDDEN;
android_mallopt(M_INITIALIZE_GWP_ASAN, &gwp_asan_options, sizeof(gwp_asan_options));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 94e01e9..5d451a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -16,8 +16,6 @@
package com.android.wm.shell;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -30,27 +28,20 @@
import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.graphics.Region;
-import android.os.Binder;
-import android.util.CloseGuard;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewTreeObserver;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import java.io.PrintWriter;
import java.util.concurrent.Executor;
/**
- * View that can display a task.
+ * A {@link SurfaceView} that can display a task. This is a concrete implementation for
+ * {@link TaskViewBase} which interacts {@link TaskViewTaskController}.
*/
public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
- ShellTaskOrganizer.TaskListener, ViewTreeObserver.OnComputeInternalInsetsListener {
-
+ ViewTreeObserver.OnComputeInternalInsetsListener, TaskViewBase {
/** Callback for listening task state. */
public interface Listener {
/**
@@ -75,65 +66,33 @@
default void onBackPressedOnTaskRoot(int taskId) {}
}
- private final CloseGuard mGuard = new CloseGuard();
-
- private final ShellTaskOrganizer mTaskOrganizer;
- private final Executor mShellExecutor;
- private final SyncTransactionQueue mSyncQueue;
- private final TaskViewTransitions mTaskViewTransitions;
-
- protected ActivityManager.RunningTaskInfo mTaskInfo;
- private WindowContainerToken mTaskToken;
- private SurfaceControl mTaskLeash;
- private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
- private boolean mSurfaceCreated;
- private boolean mIsInitialized;
- private boolean mNotifiedForInitialized;
- private Listener mListener;
- private Executor mListenerExecutor;
- private Region mObscuredTouchRegion;
-
private final Rect mTmpRect = new Rect();
private final Rect mTmpRootRect = new Rect();
private final int[] mTmpLocation = new int[2];
+ private final TaskViewTaskController mTaskViewTaskController;
+ private Region mObscuredTouchRegion;
- public TaskView(Context context, ShellTaskOrganizer organizer,
- TaskViewTransitions taskViewTransitions, SyncTransactionQueue syncQueue) {
+ public TaskView(Context context, TaskViewTaskController taskViewTaskController) {
super(context, null, 0, 0, true /* disableBackgroundLayer */);
-
- mTaskOrganizer = organizer;
- mShellExecutor = organizer.getExecutor();
- mSyncQueue = syncQueue;
- mTaskViewTransitions = taskViewTransitions;
- if (mTaskViewTransitions != null) {
- mTaskViewTransitions.addTaskView(this);
- }
+ mTaskViewTaskController = taskViewTaskController;
+ // TODO(b/266736992): Think about a better way to set the TaskViewBase on the
+ // TaskViewTaskController and vice-versa
+ mTaskViewTaskController.setTaskViewBase(this);
getHolder().addCallback(this);
- mGuard.open("release");
}
/**
- * @return {@code True} when the TaskView's surface has been created, {@code False} otherwise.
+ * Launch a new activity.
+ *
+ * @param pendingIntent Intent used to launch an activity.
+ * @param fillInIntent Additional Intent data, see {@link Intent#fillIn Intent.fillIn()}
+ * @param options options for the activity.
+ * @param launchBounds the bounds (window size and position) that the activity should be
+ * launched in, in pixels and in screen coordinates.
*/
- public boolean isInitialized() {
- return mIsInitialized;
- }
-
- /** Until all users are converted, we may have mixed-use (eg. Car). */
- private boolean isUsingShellTransitions() {
- return mTaskViewTransitions != null && mTaskViewTransitions.isEnabled();
- }
-
- /**
- * Only one listener may be set on the view, throws an exception otherwise.
- */
- public void setListener(@NonNull Executor executor, Listener listener) {
- if (mListener != null) {
- throw new IllegalStateException(
- "Trying to set a listener when one has already been set");
- }
- mListener = listener;
- mListenerExecutor = executor;
+ public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
+ @NonNull ActivityOptions options, @Nullable Rect launchBounds) {
+ mTaskViewTaskController.startActivity(pendingIntent, fillInIntent, options, launchBounds);
}
/**
@@ -148,61 +107,47 @@
*/
public void startShortcutActivity(@NonNull ShortcutInfo shortcut,
@NonNull ActivityOptions options, @Nullable Rect launchBounds) {
- prepareActivityOptions(options, launchBounds);
- LauncherApps service = mContext.getSystemService(LauncherApps.class);
- if (isUsingShellTransitions()) {
- mShellExecutor.execute(() -> {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.startShortcut(mContext.getPackageName(), shortcut, options.toBundle());
- mTaskViewTransitions.startTaskView(wct, this);
- });
- return;
+ mTaskViewTaskController.startShortcutActivity(shortcut, options, launchBounds);
+ }
+
+ @Override
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ onLocationChanged();
+ if (taskInfo.taskDescription != null) {
+ setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
}
- try {
- service.startShortcut(shortcut, null /* sourceBounds */, options.toBundle());
- } catch (Exception e) {
- throw new RuntimeException(e);
+ }
+
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (taskInfo.taskDescription != null) {
+ setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
}
}
/**
- * Launch a new activity.
- *
- * @param pendingIntent Intent used to launch an activity.
- * @param fillInIntent Additional Intent data, see {@link Intent#fillIn Intent.fillIn()}
- * @param options options for the activity.
- * @param launchBounds the bounds (window size and position) that the activity should be
- * launched in, in pixels and in screen coordinates.
+ * @return {@code True} when the TaskView's surface has been created, {@code False} otherwise.
*/
- public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
- @NonNull ActivityOptions options, @Nullable Rect launchBounds) {
- prepareActivityOptions(options, launchBounds);
- if (isUsingShellTransitions()) {
- mShellExecutor.execute(() -> {
- WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.sendPendingIntent(pendingIntent, fillInIntent, options.toBundle());
- mTaskViewTransitions.startTaskView(wct, this);
- });
- return;
- }
- try {
- pendingIntent.send(mContext, 0 /* code */, fillInIntent,
- null /* onFinished */, null /* handler */, null /* requiredPermission */,
- options.toBundle());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ public boolean isInitialized() {
+ return mTaskViewTaskController.isInitialized();
}
- private void prepareActivityOptions(ActivityOptions options, Rect launchBounds) {
- final Binder launchCookie = new Binder();
- mShellExecutor.execute(() -> {
- mTaskOrganizer.setPendingLaunchCookieListener(launchCookie, this);
- });
- options.setLaunchBounds(launchBounds);
- options.setLaunchCookie(launchCookie);
- options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- options.setRemoveWithTaskOrganizer(true);
+ @Override
+ public Rect getCurrentBoundsOnScreen() {
+ getBoundsOnScreen(mTmpRect);
+ return mTmpRect;
+ }
+
+ @Override
+ public void setResizeBgColor(SurfaceControl.Transaction t, int bgColor) {
+ setResizeBackgroundColor(t, bgColor);
+ }
+
+ /**
+ * Only one listener may be set on the view, throws an exception otherwise.
+ */
+ public void setListener(@NonNull Executor executor, TaskView.Listener listener) {
+ mTaskViewTaskController.setListener(executor, listener);
}
/**
@@ -227,251 +172,38 @@
* Call when view position or size has changed. Do not call when animating.
*/
public void onLocationChanged() {
- if (mTaskToken == null) {
- return;
- }
- // Sync Transactions can't operate simultaneously with shell transition collection.
- // The transition animation (upon showing) will sync the location itself.
- if (isUsingShellTransitions() && mTaskViewTransitions.hasPending()) return;
-
- WindowContainerTransaction wct = new WindowContainerTransaction();
- updateWindowBounds(wct);
- mSyncQueue.queue(wct);
- }
-
- private void updateWindowBounds(WindowContainerTransaction wct) {
getBoundsOnScreen(mTmpRect);
- wct.setBounds(mTaskToken, mTmpRect);
+ mTaskViewTaskController.onLocationChanged(mTmpRect);
}
/**
* Release this container if it is initialized.
*/
public void release() {
- performRelease();
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- if (mGuard != null) {
- mGuard.warnIfOpen();
- performRelease();
- }
- } finally {
- super.finalize();
- }
- }
-
- private void performRelease() {
getHolder().removeCallback(this);
- if (mTaskViewTransitions != null) {
- mTaskViewTransitions.removeTaskView(this);
- }
- mShellExecutor.execute(() -> {
- mTaskOrganizer.removeListener(this);
- resetTaskInfo();
- });
- mGuard.close();
- mIsInitialized = false;
- notifyReleased();
- }
-
- /** Called when the {@link TaskView} has been released. */
- protected void notifyReleased() {
- if (mListener != null && mNotifiedForInitialized) {
- mListenerExecutor.execute(() -> {
- mListener.onReleased();
- });
- mNotifiedForInitialized = false;
- }
- }
-
- private void resetTaskInfo() {
- mTaskInfo = null;
- mTaskToken = null;
- mTaskLeash = null;
- }
-
- private void updateTaskVisibility() {
- WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */);
- mSyncQueue.queue(wct);
- if (mListener == null) {
- return;
- }
- int taskId = mTaskInfo.taskId;
- mSyncQueue.runInSync((t) -> {
- mListenerExecutor.execute(() -> {
- mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated);
- });
- });
- }
-
- @Override
- public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
- SurfaceControl leash) {
- if (isUsingShellTransitions()) {
- // Everything else handled by enter transition.
- return;
- }
- mTaskInfo = taskInfo;
- mTaskToken = taskInfo.token;
- mTaskLeash = leash;
-
- if (mSurfaceCreated) {
- // Surface is ready, so just reparent the task to this surface control
- mTransaction.reparent(mTaskLeash, getSurfaceControl())
- .show(mTaskLeash)
- .apply();
- } else {
- // The surface has already been destroyed before the task has appeared,
- // so go ahead and hide the task entirely
- updateTaskVisibility();
- }
- mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true);
- onLocationChanged();
- if (taskInfo.taskDescription != null) {
- int backgroundColor = taskInfo.taskDescription.getBackgroundColor();
- mSyncQueue.runInSync((t) -> {
- setResizeBackgroundColor(t, backgroundColor);
- });
- }
-
- if (mListener != null) {
- final int taskId = taskInfo.taskId;
- final ComponentName baseActivity = taskInfo.baseActivity;
- mListenerExecutor.execute(() -> {
- mListener.onTaskCreated(taskId, baseActivity);
- });
- }
- }
-
- @Override
- public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- // Unlike Appeared, we can't yet guarantee that vanish will happen within a transition that
- // we know about -- so leave clean-up here even if shell transitions are enabled.
- if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
-
- if (mListener != null) {
- final int taskId = taskInfo.taskId;
- mListenerExecutor.execute(() -> {
- mListener.onTaskRemovalStarted(taskId);
- });
- }
- mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
-
- // Unparent the task when this surface is destroyed
- mTransaction.reparent(mTaskLeash, null).apply();
- resetTaskInfo();
- }
-
- @Override
- public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
- if (taskInfo.taskDescription != null) {
- setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
- }
- }
-
- @Override
- public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
- if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
- if (mListener != null) {
- final int taskId = taskInfo.taskId;
- mListenerExecutor.execute(() -> {
- mListener.onBackPressedOnTaskRoot(taskId);
- });
- }
- }
-
- @Override
- public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
- b.setParent(findTaskSurface(taskId));
- }
-
- @Override
- public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
- SurfaceControl.Transaction t) {
- t.reparent(sc, findTaskSurface(taskId));
- }
-
- private SurfaceControl findTaskSurface(int taskId) {
- if (mTaskInfo == null || mTaskLeash == null || mTaskInfo.taskId != taskId) {
- throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
- }
- return mTaskLeash;
- }
-
- @Override
- public void dump(@androidx.annotation.NonNull PrintWriter pw, String prefix) {
- final String innerPrefix = prefix + " ";
- final String childPrefix = innerPrefix + " ";
- pw.println(prefix + this);
+ mTaskViewTaskController.release();
}
@Override
public String toString() {
- return "TaskView" + ":" + (mTaskInfo != null ? mTaskInfo.taskId : "null");
+ return mTaskViewTaskController.toString();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
- mSurfaceCreated = true;
- mIsInitialized = true;
- notifyInitialized();
- mShellExecutor.execute(() -> {
- if (mTaskToken == null) {
- // Nothing to update, task is not yet available
- return;
- }
- if (isUsingShellTransitions()) {
- mTaskViewTransitions.setTaskViewVisible(this, true /* visible */);
- return;
- }
- // Reparent the task when this surface is created
- mTransaction.reparent(mTaskLeash, getSurfaceControl())
- .show(mTaskLeash)
- .apply();
- updateTaskVisibility();
- });
- }
-
- /** Called when the {@link TaskView} is initialized. */
- protected void notifyInitialized() {
- if (mListener != null && !mNotifiedForInitialized) {
- mNotifiedForInitialized = true;
- mListenerExecutor.execute(() -> {
- mListener.onInitialized();
- });
- }
+ mTaskViewTaskController.surfaceCreated(getSurfaceControl());
}
@Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- if (mTaskToken == null) {
- return;
- }
- onLocationChanged();
+ public void surfaceChanged(@androidx.annotation.NonNull SurfaceHolder holder, int format,
+ int width, int height) {
+ getBoundsOnScreen(mTmpRect);
+ mTaskViewTaskController.surfaceChanged(mTmpRect);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
- mSurfaceCreated = false;
- mShellExecutor.execute(() -> {
- if (mTaskToken == null) {
- // Nothing to update, task is not yet available
- return;
- }
-
- if (isUsingShellTransitions()) {
- mTaskViewTransitions.setTaskViewVisible(this, false /* visible */);
- return;
- }
-
- // Unparent the task when this surface is destroyed
- mTransaction.reparent(mTaskLeash, null).apply();
- updateTaskVisibility();
- });
+ mTaskViewTaskController.surfaceDestroyed();
}
@Override
@@ -513,89 +245,6 @@
/** Returns the task info for the task in the TaskView. */
@Nullable
public ActivityManager.RunningTaskInfo getTaskInfo() {
- return mTaskInfo;
- }
-
- void prepareHideAnimation(@NonNull SurfaceControl.Transaction finishTransaction) {
- if (mTaskToken == null) {
- // Nothing to update, task is not yet available
- return;
- }
-
- finishTransaction.reparent(mTaskLeash, null).apply();
-
- if (mListener != null) {
- final int taskId = mTaskInfo.taskId;
- mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated /* visible */);
- }
- }
-
- /**
- * Called when the associated Task closes. If the TaskView is just being hidden, prepareHide
- * is used instead.
- */
- void prepareCloseAnimation() {
- if (mTaskToken != null) {
- if (mListener != null) {
- final int taskId = mTaskInfo.taskId;
- mListenerExecutor.execute(() -> {
- mListener.onTaskRemovalStarted(taskId);
- });
- }
- mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
- }
- resetTaskInfo();
- }
-
- void prepareOpenAnimation(final boolean newTask,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash,
- WindowContainerTransaction wct) {
- mTaskInfo = taskInfo;
- mTaskToken = mTaskInfo.token;
- mTaskLeash = leash;
- if (mSurfaceCreated) {
- // Surface is ready, so just reparent the task to this surface control
- startTransaction.reparent(mTaskLeash, getSurfaceControl())
- .show(mTaskLeash)
- .apply();
- // Also reparent on finishTransaction since the finishTransaction will reparent back
- // to its "original" parent by default.
- finishTransaction.reparent(mTaskLeash, getSurfaceControl())
- .setPosition(mTaskLeash, 0, 0)
- .apply();
-
- // TODO: determine if this is really necessary or not
- updateWindowBounds(wct);
- } else {
- // The surface has already been destroyed before the task has appeared,
- // so go ahead and hide the task entirely
- wct.setHidden(mTaskToken, true /* hidden */);
- // listener callback is below
- }
- if (newTask) {
- mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true /* intercept */);
- }
-
- if (mTaskInfo.taskDescription != null) {
- int backgroundColor = mTaskInfo.taskDescription.getBackgroundColor();
- setResizeBackgroundColor(startTransaction, backgroundColor);
- }
-
- if (mListener != null) {
- final int taskId = mTaskInfo.taskId;
- final ComponentName baseActivity = mTaskInfo.baseActivity;
-
- mListenerExecutor.execute(() -> {
- if (newTask) {
- mListener.onTaskCreated(taskId, baseActivity);
- }
- // Even if newTask, send a visibilityChange if the surface was destroyed.
- if (!newTask || !mSurfaceCreated) {
- mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated /* visible */);
- }
- });
- }
+ return mTaskViewTaskController.getTaskInfo();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewBase.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewBase.java
new file mode 100644
index 0000000..3d0a8fd
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewBase.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell;
+
+import android.app.ActivityManager;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+
+/**
+ * A stub for SurfaceView used by {@link TaskViewTaskController}
+ */
+public interface TaskViewBase {
+ /**
+ * Returns the current bounds on screen for the task view.
+ * @return
+ */
+ // TODO(b/266242294): Remove getBoundsOnScreen() and instead send the bounds from the TaskView
+ // to TaskViewTaskController.
+ Rect getCurrentBoundsOnScreen();
+
+ /**
+ * This method should set the resize background color on the SurfaceView that is exposed to
+ * clients.
+ * See {@link android.view.SurfaceView#setResizeBackgroundColor(SurfaceControl.Transaction,
+ * int)}
+ */
+ void setResizeBgColor(SurfaceControl.Transaction transaction, int color);
+
+ /**
+ * Called when a task appears on the TaskView. See
+ * {@link TaskViewTaskController#onTaskAppeared(ActivityManager.RunningTaskInfo,
+ * SurfaceControl)} for details.
+ */
+ default void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ }
+
+ /**
+ * Called when a task is vanished from the TaskView. See
+ * {@link TaskViewTaskController#onTaskVanished(ActivityManager.RunningTaskInfo)} for details.
+ */
+ default void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ }
+
+ /**
+ * Called when the task in the TaskView is changed. See
+ * {@link TaskViewTaskController#onTaskInfoChanged(ActivityManager.RunningTaskInfo)} for details.
+ */
+ default void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
index 42844b5..735d9bc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
@@ -57,7 +57,8 @@
/** Creates an {@link TaskView} */
public void create(@UiContext Context context, Executor executor, Consumer<TaskView> onCreate) {
- TaskView taskView = new TaskView(context, mTaskOrganizer, mTaskViewTransitions, mSyncQueue);
+ TaskView taskView = new TaskView(context, new TaskViewTaskController(context,
+ mTaskOrganizer, mTaskViewTransitions, mSyncQueue));
executor.execute(() -> {
onCreate.accept(taskView);
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTaskController.java
new file mode 100644
index 0000000..69ce35f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTaskController.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2020 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.wm.shell;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.util.CloseGuard;
+import android.view.SurfaceControl;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import java.io.PrintWriter;
+import java.util.concurrent.Executor;
+
+/**
+ * This class implements the core logic to show a task on the {@link TaskView}. All the {@link
+ * TaskView} to {@link TaskViewTaskController} interactions are done via direct method calls.
+ *
+ * The reverse communication is done via the {@link TaskViewBase} interface.
+ */
+public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
+
+ private final CloseGuard mGuard = new CloseGuard();
+
+ private final ShellTaskOrganizer mTaskOrganizer;
+ private final Executor mShellExecutor;
+ private final SyncTransactionQueue mSyncQueue;
+ private final TaskViewTransitions mTaskViewTransitions;
+ private TaskViewBase mTaskViewBase;
+ private final Context mContext;
+
+ protected ActivityManager.RunningTaskInfo mTaskInfo;
+ private WindowContainerToken mTaskToken;
+ private SurfaceControl mTaskLeash;
+ private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+ private boolean mSurfaceCreated;
+ private SurfaceControl mSurfaceControl;
+ private boolean mIsInitialized;
+ private boolean mNotifiedForInitialized;
+ private TaskView.Listener mListener;
+ private Executor mListenerExecutor;
+
+ public TaskViewTaskController(Context context, ShellTaskOrganizer organizer,
+ TaskViewTransitions taskViewTransitions, SyncTransactionQueue syncQueue) {
+ mContext = context;
+ mTaskOrganizer = organizer;
+ mShellExecutor = organizer.getExecutor();
+ mSyncQueue = syncQueue;
+ mTaskViewTransitions = taskViewTransitions;
+ if (mTaskViewTransitions != null) {
+ mTaskViewTransitions.addTaskView(this);
+ }
+ mGuard.open("release");
+ }
+
+ /**
+ * Sets the provided {@link TaskViewBase}, which is used to notify the client part about the
+ * task related changes and getting the current bounds.
+ */
+ public void setTaskViewBase(TaskViewBase taskViewBase) {
+ mTaskViewBase = taskViewBase;
+ }
+
+ /**
+ * @return {@code True} when the TaskView's surface has been created, {@code False} otherwise.
+ */
+ public boolean isInitialized() {
+ return mIsInitialized;
+ }
+
+ /** Until all users are converted, we may have mixed-use (eg. Car). */
+ private boolean isUsingShellTransitions() {
+ return mTaskViewTransitions != null && mTaskViewTransitions.isEnabled();
+ }
+
+ /**
+ * Only one listener may be set on the view, throws an exception otherwise.
+ */
+ void setListener(@NonNull Executor executor, TaskView.Listener listener) {
+ if (mListener != null) {
+ throw new IllegalStateException(
+ "Trying to set a listener when one has already been set");
+ }
+ mListener = listener;
+ mListenerExecutor = executor;
+ }
+
+ /**
+ * Launch an activity represented by {@link ShortcutInfo}.
+ * <p>The owner of this container must be allowed to access the shortcut information,
+ * as defined in {@link LauncherApps#hasShortcutHostPermission()} to use this method.
+ *
+ * @param shortcut the shortcut used to launch the activity.
+ * @param options options for the activity.
+ * @param launchBounds the bounds (window size and position) that the activity should be
+ * launched in, in pixels and in screen coordinates.
+ */
+ public void startShortcutActivity(@NonNull ShortcutInfo shortcut,
+ @NonNull ActivityOptions options, @Nullable Rect launchBounds) {
+ prepareActivityOptions(options, launchBounds);
+ LauncherApps service = mContext.getSystemService(LauncherApps.class);
+ if (isUsingShellTransitions()) {
+ mShellExecutor.execute(() -> {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.startShortcut(mContext.getPackageName(), shortcut, options.toBundle());
+ mTaskViewTransitions.startTaskView(wct, this);
+ });
+ return;
+ }
+ try {
+ service.startShortcut(shortcut, null /* sourceBounds */, options.toBundle());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Launch a new activity.
+ *
+ * @param pendingIntent Intent used to launch an activity.
+ * @param fillInIntent Additional Intent data, see {@link Intent#fillIn Intent.fillIn()}
+ * @param options options for the activity.
+ * @param launchBounds the bounds (window size and position) that the activity should be
+ * launched in, in pixels and in screen coordinates.
+ */
+ public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
+ @NonNull ActivityOptions options, @Nullable Rect launchBounds) {
+ prepareActivityOptions(options, launchBounds);
+ if (isUsingShellTransitions()) {
+ mShellExecutor.execute(() -> {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.sendPendingIntent(pendingIntent, fillInIntent, options.toBundle());
+ mTaskViewTransitions.startTaskView(wct, this);
+ });
+ return;
+ }
+ try {
+ pendingIntent.send(mContext, 0 /* code */, fillInIntent,
+ null /* onFinished */, null /* handler */, null /* requiredPermission */,
+ options.toBundle());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void prepareActivityOptions(ActivityOptions options, Rect launchBounds) {
+ final Binder launchCookie = new Binder();
+ mShellExecutor.execute(() -> {
+ mTaskOrganizer.setPendingLaunchCookieListener(launchCookie, this);
+ });
+ options.setLaunchBounds(launchBounds);
+ options.setLaunchCookie(launchCookie);
+ options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ options.setRemoveWithTaskOrganizer(true);
+ }
+
+ /**
+ * Call when view position or size has changed. Do not call when animating.
+ */
+ public void onLocationChanged(Rect newBounds) {
+ if (mTaskToken == null) {
+ return;
+ }
+ // Sync Transactions can't operate simultaneously with shell transition collection.
+ // The transition animation (upon showing) will sync the location itself.
+ if (isUsingShellTransitions() && mTaskViewTransitions.hasPending()) return;
+
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ updateWindowBounds(wct);
+ mSyncQueue.queue(wct);
+ }
+
+ private void updateWindowBounds(WindowContainerTransaction wct) {
+ wct.setBounds(mTaskToken, mTaskViewBase.getCurrentBoundsOnScreen());
+ }
+
+ /**
+ * Release this container if it is initialized.
+ */
+ public void release() {
+ performRelease();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mGuard != null) {
+ mGuard.warnIfOpen();
+ performRelease();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private void performRelease() {
+ if (mTaskViewTransitions != null) {
+ mTaskViewTransitions.removeTaskView(this);
+ }
+ mShellExecutor.execute(() -> {
+ mTaskOrganizer.removeListener(this);
+ resetTaskInfo();
+ });
+ mGuard.close();
+ mIsInitialized = false;
+ notifyReleased();
+ }
+
+ /** Called when the {@link TaskViewTaskController} has been released. */
+ protected void notifyReleased() {
+ if (mListener != null && mNotifiedForInitialized) {
+ mListenerExecutor.execute(() -> {
+ mListener.onReleased();
+ });
+ mNotifiedForInitialized = false;
+ }
+ }
+
+ private void resetTaskInfo() {
+ mTaskInfo = null;
+ mTaskToken = null;
+ mTaskLeash = null;
+ }
+
+ private void updateTaskVisibility() {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */);
+ mSyncQueue.queue(wct);
+ if (mListener == null) {
+ return;
+ }
+ int taskId = mTaskInfo.taskId;
+ mSyncQueue.runInSync((t) -> {
+ mListenerExecutor.execute(() -> {
+ mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated);
+ });
+ });
+ }
+
+ @Override
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl leash) {
+ if (isUsingShellTransitions()) {
+ // Everything else handled by enter transition.
+ return;
+ }
+ mTaskInfo = taskInfo;
+ mTaskToken = taskInfo.token;
+ mTaskLeash = leash;
+
+ if (mSurfaceCreated) {
+ // Surface is ready, so just reparent the task to this surface control
+ mTransaction.reparent(mTaskLeash, mSurfaceControl)
+ .show(mTaskLeash)
+ .apply();
+ } else {
+ // The surface has already been destroyed before the task has appeared,
+ // so go ahead and hide the task entirely
+ updateTaskVisibility();
+ }
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true);
+ mSyncQueue.runInSync((t) -> {
+ mTaskViewBase.onTaskAppeared(taskInfo, leash);
+ });
+
+ if (mListener != null) {
+ final int taskId = taskInfo.taskId;
+ final ComponentName baseActivity = taskInfo.baseActivity;
+ mListenerExecutor.execute(() -> {
+ mListener.onTaskCreated(taskId, baseActivity);
+ });
+ }
+ }
+
+ @Override
+ public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ // Unlike Appeared, we can't yet guarantee that vanish will happen within a transition that
+ // we know about -- so leave clean-up here even if shell transitions are enabled.
+ if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
+
+ if (mListener != null) {
+ final int taskId = taskInfo.taskId;
+ mListenerExecutor.execute(() -> {
+ mListener.onTaskRemovalStarted(taskId);
+ });
+ }
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
+
+ // Unparent the task when this surface is destroyed
+ mTransaction.reparent(mTaskLeash, null).apply();
+ resetTaskInfo();
+ mTaskViewBase.onTaskVanished(taskInfo);
+ }
+
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ mTaskViewBase.onTaskInfoChanged(taskInfo);
+ }
+
+ @Override
+ public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
+ if (mListener != null) {
+ final int taskId = taskInfo.taskId;
+ mListenerExecutor.execute(() -> {
+ mListener.onBackPressedOnTaskRoot(taskId);
+ });
+ }
+ }
+
+ @Override
+ public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
+ b.setParent(findTaskSurface(taskId));
+ }
+
+ @Override
+ public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
+ SurfaceControl.Transaction t) {
+ t.reparent(sc, findTaskSurface(taskId));
+ }
+
+ private SurfaceControl findTaskSurface(int taskId) {
+ if (mTaskInfo == null || mTaskLeash == null || mTaskInfo.taskId != taskId) {
+ throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
+ }
+ return mTaskLeash;
+ }
+
+ @Override
+ public void dump(@androidx.annotation.NonNull PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ final String childPrefix = innerPrefix + " ";
+ pw.println(prefix + this);
+ }
+
+ @Override
+ public String toString() {
+ return "TaskViewTaskController" + ":" + (mTaskInfo != null ? mTaskInfo.taskId : "null");
+ }
+
+ /**
+ * Should be called when the client surface is created.
+ *
+ * @param surfaceControl the {@link SurfaceControl} for the underlying surface.
+ */
+ public void surfaceCreated(SurfaceControl surfaceControl) {
+ mSurfaceCreated = true;
+ mIsInitialized = true;
+ mSurfaceControl = surfaceControl;
+ notifyInitialized();
+ mShellExecutor.execute(() -> {
+ if (mTaskToken == null) {
+ // Nothing to update, task is not yet available
+ return;
+ }
+ if (isUsingShellTransitions()) {
+ mTaskViewTransitions.setTaskViewVisible(this, true /* visible */);
+ return;
+ }
+ // Reparent the task when this surface is created
+ mTransaction.reparent(mTaskLeash, mSurfaceControl)
+ .show(mTaskLeash)
+ .apply();
+ updateTaskVisibility();
+ });
+ }
+
+ /**
+ * Should be called when the client surface is changed.
+ *
+ * @param boundsOnScreen the on screen bounds of the surface view.
+ */
+ public void surfaceChanged(Rect boundsOnScreen) {
+ if (mTaskToken == null) {
+ return;
+ }
+ onLocationChanged(boundsOnScreen);
+ }
+
+ /** Should be called when the client surface is destroyed. */
+ public void surfaceDestroyed() {
+ mSurfaceCreated = false;
+ mSurfaceControl = null;
+ mShellExecutor.execute(() -> {
+ if (mTaskToken == null) {
+ // Nothing to update, task is not yet available
+ return;
+ }
+
+ if (isUsingShellTransitions()) {
+ mTaskViewTransitions.setTaskViewVisible(this, false /* visible */);
+ return;
+ }
+
+ // Unparent the task when this surface is destroyed
+ mTransaction.reparent(mTaskLeash, null).apply();
+ updateTaskVisibility();
+ });
+ }
+
+ /** Called when the {@link TaskViewTaskController} is initialized. */
+ protected void notifyInitialized() {
+ if (mListener != null && !mNotifiedForInitialized) {
+ mNotifiedForInitialized = true;
+ mListenerExecutor.execute(() -> {
+ mListener.onInitialized();
+ });
+ }
+ }
+
+ /** Returns the task info for the task in the TaskView. */
+ @Nullable
+ public ActivityManager.RunningTaskInfo getTaskInfo() {
+ return mTaskInfo;
+ }
+
+ void prepareHideAnimation(@NonNull SurfaceControl.Transaction finishTransaction) {
+ if (mTaskToken == null) {
+ // Nothing to update, task is not yet available
+ return;
+ }
+
+ finishTransaction.reparent(mTaskLeash, null).apply();
+
+ if (mListener != null) {
+ final int taskId = mTaskInfo.taskId;
+ mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated /* visible */);
+ }
+ }
+
+ /**
+ * Called when the associated Task closes. If the TaskView is just being hidden, prepareHide
+ * is used instead.
+ */
+ void prepareCloseAnimation() {
+ if (mTaskToken != null) {
+ if (mListener != null) {
+ final int taskId = mTaskInfo.taskId;
+ mListenerExecutor.execute(() -> {
+ mListener.onTaskRemovalStarted(taskId);
+ });
+ }
+ mTaskViewBase.onTaskVanished(mTaskInfo);
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
+ }
+ resetTaskInfo();
+ }
+
+ void prepareOpenAnimation(final boolean newTask,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash,
+ WindowContainerTransaction wct) {
+ mTaskInfo = taskInfo;
+ mTaskToken = mTaskInfo.token;
+ mTaskLeash = leash;
+ if (mSurfaceCreated) {
+ // Surface is ready, so just reparent the task to this surface control
+ startTransaction.reparent(mTaskLeash, mSurfaceControl)
+ .show(mTaskLeash)
+ .apply();
+ // Also reparent on finishTransaction since the finishTransaction will reparent back
+ // to its "original" parent by default.
+ finishTransaction.reparent(mTaskLeash, mSurfaceControl)
+ .setPosition(mTaskLeash, 0, 0)
+ .apply();
+
+ updateWindowBounds(wct);
+ } else {
+ // The surface has already been destroyed before the task has appeared,
+ // so go ahead and hide the task entirely
+ wct.setHidden(mTaskToken, true /* hidden */);
+ // listener callback is below
+ }
+ if (newTask) {
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true /* intercept */);
+ }
+
+ if (mTaskInfo.taskDescription != null) {
+ int backgroundColor = mTaskInfo.taskDescription.getBackgroundColor();
+ mTaskViewBase.setResizeBgColor(startTransaction, backgroundColor);
+ }
+
+ if (mListener != null) {
+ final int taskId = mTaskInfo.taskId;
+ final ComponentName baseActivity = mTaskInfo.baseActivity;
+
+ mListenerExecutor.execute(() -> {
+ if (newTask) {
+ mListener.onTaskCreated(taskId, baseActivity);
+ }
+ // Even if newTask, send a visibilityChange if the surface was destroyed.
+ if (!newTask || !mSurfaceCreated) {
+ mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated /* visible */);
+ }
+ });
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
index 07d5012..aab257e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
@@ -41,7 +41,7 @@
public class TaskViewTransitions implements Transitions.TransitionHandler {
private static final String TAG = "TaskViewTransitions";
- private final ArrayList<TaskView> mTaskViews = new ArrayList<>();
+ private final ArrayList<TaskViewTaskController> mTaskViews = new ArrayList<>();
private final ArrayList<PendingTransition> mPending = new ArrayList<>();
private final Transitions mTransitions;
private final boolean[] mRegistered = new boolean[]{ false };
@@ -54,11 +54,12 @@
private static class PendingTransition {
final @WindowManager.TransitionType int mType;
final WindowContainerTransaction mWct;
- final @NonNull TaskView mTaskView;
+ final @NonNull TaskViewTaskController mTaskView;
IBinder mClaimed;
PendingTransition(@WindowManager.TransitionType int type,
- @Nullable WindowContainerTransaction wct, @NonNull TaskView taskView) {
+ @Nullable WindowContainerTransaction wct,
+ @NonNull TaskViewTaskController taskView) {
mType = type;
mWct = wct;
mTaskView = taskView;
@@ -72,7 +73,7 @@
// TODO(210041388): register here once we have an explicit ordering mechanism.
}
- void addTaskView(TaskView tv) {
+ void addTaskView(TaskViewTaskController tv) {
synchronized (mRegistered) {
if (!mRegistered[0]) {
mRegistered[0] = true;
@@ -82,7 +83,7 @@
mTaskViews.add(tv);
}
- void removeTaskView(TaskView tv) {
+ void removeTaskView(TaskViewTaskController tv) {
mTaskViews.remove(tv);
// Note: Don't unregister handler since this is a singleton with lifetime bound to Shell
}
@@ -101,7 +102,8 @@
* if there is a match earlier. The idea behind this is to check the state of
* the taskviews "as if all transitions already happened".
*/
- private PendingTransition findPending(TaskView taskView, boolean closing, boolean latest) {
+ private PendingTransition findPending(TaskViewTaskController taskView, boolean closing,
+ boolean latest) {
for (int i = mPending.size() - 1; i >= 0; --i) {
if (mPending.get(i).mTaskView != taskView) continue;
if (Transitions.isClosingType(mPending.get(i).mType) == closing) {
@@ -134,7 +136,7 @@
if (triggerTask == null) {
return null;
}
- final TaskView taskView = findTaskView(triggerTask);
+ final TaskViewTaskController taskView = findTaskView(triggerTask);
if (taskView == null) return null;
// Opening types should all be initiated by shell
if (!Transitions.isClosingType(request.getType())) return null;
@@ -150,7 +152,7 @@
return new WindowContainerTransaction();
}
- private TaskView findTaskView(ActivityManager.RunningTaskInfo taskInfo) {
+ private TaskViewTaskController findTaskView(ActivityManager.RunningTaskInfo taskInfo) {
for (int i = 0; i < mTaskViews.size(); ++i) {
if (mTaskViews.get(i).getTaskInfo() == null) continue;
if (taskInfo.token.equals(mTaskViews.get(i).getTaskInfo().token)) {
@@ -160,12 +162,12 @@
return null;
}
- void startTaskView(WindowContainerTransaction wct, TaskView taskView) {
+ void startTaskView(WindowContainerTransaction wct, TaskViewTaskController taskView) {
mPending.add(new PendingTransition(TRANSIT_OPEN, wct, taskView));
startNextTransition();
}
- void setTaskViewVisible(TaskView taskView, boolean visible) {
+ void setTaskViewVisible(TaskViewTaskController taskView, boolean visible) {
PendingTransition pending = findPending(taskView, !visible, true /* latest */);
if (pending != null) {
// Already opening or creating a task, so no need to do anything here.
@@ -203,7 +205,7 @@
PendingTransition pending = findPending(transition);
if (pending == null) return false;
mPending.remove(pending);
- TaskView taskView = pending.mTaskView;
+ TaskViewTaskController taskView = pending.mTaskView;
final ArrayList<TransitionInfo.Change> tasks = new ArrayList<>();
for (int i = 0; i < info.getChanges().size(); ++i) {
final TransitionInfo.Change chg = info.getChanges().get(i);
@@ -219,7 +221,7 @@
TransitionInfo.Change chg = tasks.get(i);
if (Transitions.isClosingType(chg.getMode())) {
final boolean isHide = chg.getMode() == TRANSIT_TO_BACK;
- TaskView tv = findTaskView(chg.getTaskInfo());
+ TaskViewTaskController tv = findTaskView(chg.getTaskInfo());
if (tv == null) {
throw new IllegalStateException("TaskView transition is closing a "
+ "non-taskview task ");
@@ -232,7 +234,7 @@
} else if (Transitions.isOpeningType(chg.getMode())) {
final boolean taskIsNew = chg.getMode() == TRANSIT_OPEN;
if (wct == null) wct = new WindowContainerTransaction();
- TaskView tv = taskView;
+ TaskViewTaskController tv = taskView;
if (!taskIsNew) {
tv = findTaskView(chg.getTaskInfo());
if (tv == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index e85b3c7..1feff18 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -67,6 +67,7 @@
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.wm.shell.R;
import com.android.wm.shell.TaskView;
+import com.android.wm.shell.TaskViewTaskController;
import com.android.wm.shell.common.AlphaOptimizedButton;
import com.android.wm.shell.common.TriangleShape;
@@ -140,6 +141,7 @@
private AlphaOptimizedButton mManageButton;
private TaskView mTaskView;
+ private TaskViewTaskController mTaskViewTaskController;
private BubbleOverflowContainerView mOverflowView;
private int mTaskId = INVALID_TASK_ID;
@@ -409,8 +411,10 @@
bringChildToFront(mOverflowView);
mManageButton.setVisibility(GONE);
} else {
- mTaskView = new TaskView(mContext, mController.getTaskOrganizer(),
+ mTaskViewTaskController = new TaskViewTaskController(mContext,
+ mController.getTaskOrganizer(),
mController.getTaskViewTransitions(), mController.getSyncTransactionQueue());
+ mTaskView = new TaskView(mContext, mTaskViewTaskController);
mTaskView.setListener(mController.getMainExecutor(), mTaskViewListener);
mExpandedViewContainer.addView(mTaskView);
bringChildToFront(mTaskView);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index f77ac81..476a7ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -174,7 +174,7 @@
final CaptionTouchEventListener touchEventListener =
new CaptionTouchEventListener(taskInfo, taskPositioner);
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
- windowDecoration.setDragResizeCallback(taskPositioner);
+ windowDecoration.setDragPositioningCallback(taskPositioner);
windowDecoration.setDragDetector(touchEventListener.mDragDetector);
windowDecoration.relayout(taskInfo, startT, finishT);
setupCaptionColor(taskInfo, windowDecoration);
@@ -185,17 +185,17 @@
private final int mTaskId;
private final WindowContainerToken mTaskToken;
- private final DragResizeCallback mDragResizeCallback;
+ private final DragPositioningCallback mDragPositioningCallback;
private final DragDetector mDragDetector;
private int mDragPointerId = -1;
private CaptionTouchEventListener(
RunningTaskInfo taskInfo,
- DragResizeCallback dragResizeCallback) {
+ DragPositioningCallback dragPositioningCallback) {
mTaskId = taskInfo.taskId;
mTaskToken = taskInfo.token;
- mDragResizeCallback = dragResizeCallback;
+ mDragPositioningCallback = dragPositioningCallback;
mDragDetector = new DragDetector(this);
}
@@ -247,20 +247,20 @@
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
mDragPointerId = e.getPointerId(0);
- mDragResizeCallback.onDragResizeStart(
+ mDragPositioningCallback.onDragPositioningStart(
0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
break;
}
case MotionEvent.ACTION_MOVE: {
int dragPointerIdx = e.findPointerIndex(mDragPointerId);
- mDragResizeCallback.onDragResizeMove(
+ mDragPositioningCallback.onDragPositioningMove(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
int dragPointerIdx = e.findPointerIndex(mDragPointerId);
- mDragResizeCallback.onDragResizeEnd(
+ mDragPositioningCallback.onDragPositioningEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
break;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index f94fbfc..060dc4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -47,7 +47,7 @@
private View.OnClickListener mOnCaptionButtonClickListener;
private View.OnTouchListener mOnCaptionTouchListener;
- private DragResizeCallback mDragResizeCallback;
+ private DragPositioningCallback mDragPositioningCallback;
private DragResizeInputListener mDragResizeListener;
private DragDetector mDragDetector;
@@ -78,8 +78,8 @@
mOnCaptionTouchListener = onCaptionTouchListener;
}
- void setDragResizeCallback(DragResizeCallback dragResizeCallback) {
- mDragResizeCallback = dragResizeCallback;
+ void setDragPositioningCallback(DragPositioningCallback dragPositioningCallback) {
+ mDragPositioningCallback = dragPositioningCallback;
}
void setDragDetector(DragDetector dragDetector) {
@@ -151,7 +151,7 @@
mChoreographer,
mDisplay.getDisplayId(),
mDecorationContainerSurface,
- mDragResizeCallback);
+ mDragPositioningCallback);
}
final int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext())
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index c0c0ab9..606cf28 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -209,17 +209,17 @@
private final int mTaskId;
private final WindowContainerToken mTaskToken;
- private final DragResizeCallback mDragResizeCallback;
+ private final DragPositioningCallback mDragPositioningCallback;
private final DragDetector mDragDetector;
private int mDragPointerId = -1;
private DesktopModeTouchEventListener(
RunningTaskInfo taskInfo,
- DragResizeCallback dragResizeCallback) {
+ DragPositioningCallback dragPositioningCallback) {
mTaskId = taskInfo.taskId;
mTaskToken = taskInfo.token;
- mDragResizeCallback = dragResizeCallback;
+ mDragPositioningCallback = dragPositioningCallback;
mDragDetector = new DragDetector(this);
}
@@ -283,13 +283,13 @@
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
mDragPointerId = e.getPointerId(0);
- mDragResizeCallback.onDragResizeStart(
+ mDragPositioningCallback.onDragPositioningStart(
0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
break;
}
case MotionEvent.ACTION_MOVE: {
final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
- mDragResizeCallback.onDragResizeMove(
+ mDragPositioningCallback.onDragPositioningMove(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
break;
}
@@ -298,7 +298,7 @@
final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
final int statusBarHeight = mDisplayController
.getDisplayLayout(taskInfo.displayId).stableInsets().top;
- mDragResizeCallback.onDragResizeEnd(
+ mDragPositioningCallback.onDragPositioningEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
if (e.getRawY(dragPointerIdx) <= statusBarHeight) {
if (DesktopModeStatus.isProto2Enabled()) {
@@ -557,7 +557,7 @@
final DesktopModeTouchEventListener touchEventListener =
new DesktopModeTouchEventListener(taskInfo, taskPositioner);
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
- windowDecoration.setDragResizeCallback(taskPositioner);
+ windowDecoration.setDragPositioningCallback(taskPositioner);
windowDecoration.setDragDetector(touchEventListener.mDragDetector);
windowDecoration.relayout(taskInfo, startT, finishT);
incrementEventReceiverTasks(taskInfo.displayId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 31b56d3..744c18f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -54,7 +54,7 @@
private View.OnClickListener mOnCaptionButtonClickListener;
private View.OnTouchListener mOnCaptionTouchListener;
- private DragResizeCallback mDragResizeCallback;
+ private DragPositioningCallback mDragPositioningCallback;
private DragResizeInputListener mDragResizeListener;
private DragDetector mDragDetector;
@@ -90,8 +90,8 @@
mOnCaptionTouchListener = onCaptionTouchListener;
}
- void setDragResizeCallback(DragResizeCallback dragResizeCallback) {
- mDragResizeCallback = dragResizeCallback;
+ void setDragPositioningCallback(DragPositioningCallback dragPositioningCallback) {
+ mDragPositioningCallback = dragPositioningCallback;
}
void setDragDetector(DragDetector dragDetector) {
@@ -179,7 +179,7 @@
mChoreographer,
mDisplay.getDisplayId(),
mDecorationContainerSurface,
- mDragResizeCallback);
+ mDragPositioningCallback);
}
final int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext())
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
similarity index 76%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeCallback.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
index ee160a1..0191c60 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
@@ -19,28 +19,28 @@
/**
* Callback called when receiving drag-resize or drag-move related input events.
*/
-public interface DragResizeCallback {
+public interface DragPositioningCallback {
/**
- * Called when a drag resize starts.
+ * Called when a drag-resize or drag-move starts.
*
* @param ctrlType {@link TaskPositioner.CtrlType} indicating the direction of resizing, use
* {@code 0} to indicate it's a move
- * @param x x coordinate in window decoration coordinate system where the drag resize starts
- * @param y y coordinate in window decoration coordinate system where the drag resize starts
+ * @param x x coordinate in window decoration coordinate system where the drag starts
+ * @param y y coordinate in window decoration coordinate system where the drag starts
*/
- void onDragResizeStart(@TaskPositioner.CtrlType int ctrlType, float x, float y);
+ void onDragPositioningStart(@TaskPositioner.CtrlType int ctrlType, float x, float y);
/**
- * Called when the pointer moves during a drag resize.
+ * Called when the pointer moves during a drag-resize or drag-move.
* @param x x coordinate in window decoration coordinate system of the new pointer location
* @param y y coordinate in window decoration coordinate system of the new pointer location
*/
- void onDragResizeMove(float x, float y);
+ void onDragPositioningMove(float x, float y);
/**
- * Called when a drag resize stops.
+ * Called when a drag-resize or drag-move stops.
* @param x x coordinate in window decoration coordinate system where the drag resize stops
* @param y y coordinate in window decoration coordinate system where the drag resize stops
*/
- void onDragResizeEnd(float x, float y);
+ void onDragPositioningEnd(float x, float y);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index ea417b1..3d1edc9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -62,7 +62,7 @@
private final SurfaceControl mDecorationSurface;
private final InputChannel mInputChannel;
private final TaskResizeInputEventReceiver mInputEventReceiver;
- private final com.android.wm.shell.windowdecor.DragResizeCallback mCallback;
+ private final DragPositioningCallback mCallback;
private int mWidth;
private int mHeight;
@@ -83,7 +83,7 @@
Choreographer choreographer,
int displayId,
SurfaceControl decorationSurface,
- DragResizeCallback callback) {
+ DragPositioningCallback callback) {
mInputManager = context.getSystemService(InputManager.class);
mHandler = handler;
mChoreographer = choreographer;
@@ -294,7 +294,7 @@
float rawX = e.getRawX(0);
float rawY = e.getRawY(0);
int ctrlType = calculateCtrlType(isTouch, x, y);
- mCallback.onDragResizeStart(ctrlType, rawX, rawY);
+ mCallback.onDragPositioningStart(ctrlType, rawX, rawY);
result = true;
}
break;
@@ -306,7 +306,7 @@
int dragPointerIndex = e.findPointerIndex(mDragPointerId);
float rawX = e.getRawX(dragPointerIndex);
float rawY = e.getRawY(dragPointerIndex);
- mCallback.onDragResizeMove(rawX, rawY);
+ mCallback.onDragPositioningMove(rawX, rawY);
result = true;
break;
}
@@ -314,7 +314,7 @@
case MotionEvent.ACTION_CANCEL: {
if (mShouldHandleEvents) {
int dragPointerIndex = e.findPointerIndex(mDragPointerId);
- mCallback.onDragResizeEnd(
+ mCallback.onDragPositioningEnd(
e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
}
mShouldHandleEvents = false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
index 8cd2a59..d3f9227 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
@@ -23,7 +23,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
-class TaskPositioner implements DragResizeCallback {
+class TaskPositioner implements DragPositioningCallback {
@IntDef({CTRL_TYPE_UNDEFINED, CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT, CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM})
@interface CtrlType {}
@@ -38,8 +38,8 @@
private final WindowDecoration mWindowDecoration;
private final Rect mTaskBoundsAtDragStart = new Rect();
- private final PointF mResizeStartPoint = new PointF();
- private final Rect mResizeTaskBounds = new Rect();
+ private final PointF mRepositionStartPoint = new PointF();
+ private final Rect mRepositionTaskBounds = new Rect();
private boolean mHasMoved = false;
private int mCtrlType;
@@ -57,7 +57,7 @@
}
@Override
- public void onDragResizeStart(int ctrlType, float x, float y) {
+ public void onDragPositioningStart(int ctrlType, float x, float y) {
mHasMoved = false;
mDragStartListener.onDragStart(mWindowDecoration.mTaskInfo.taskId);
@@ -65,11 +65,11 @@
mTaskBoundsAtDragStart.set(
mWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
- mResizeStartPoint.set(x, y);
+ mRepositionStartPoint.set(x, y);
}
@Override
- public void onDragResizeMove(float x, float y) {
+ public void onDragPositioningMove(float x, float y) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (changeBounds(wct, x, y)) {
// The task is being resized, send the |dragResizing| hint to core with the first
@@ -84,7 +84,7 @@
}
@Override
- public void onDragResizeEnd(float x, float y) {
+ public void onDragPositioningEnd(float x, float y) {
// |mHasMoved| being false means there is no real change to the task bounds in WM core, so
// we don't need a WCT to finish it.
if (mHasMoved) {
@@ -96,42 +96,44 @@
mCtrlType = CTRL_TYPE_UNDEFINED;
mTaskBoundsAtDragStart.setEmpty();
- mResizeStartPoint.set(0, 0);
+ mRepositionStartPoint.set(0, 0);
mHasMoved = false;
}
private boolean changeBounds(WindowContainerTransaction wct, float x, float y) {
- // |mResizeTaskBounds| is the bounds last reported if |mHasMoved| is true. If it's not true,
- // we can compare it against |mTaskBoundsAtDragStart|.
- final int oldLeft = mHasMoved ? mResizeTaskBounds.left : mTaskBoundsAtDragStart.left;
- final int oldTop = mHasMoved ? mResizeTaskBounds.top : mTaskBoundsAtDragStart.top;
- final int oldRight = mHasMoved ? mResizeTaskBounds.right : mTaskBoundsAtDragStart.right;
- final int oldBottom = mHasMoved ? mResizeTaskBounds.bottom : mTaskBoundsAtDragStart.bottom;
+ // |mRepositionTaskBounds| is the bounds last reported if |mHasMoved| is true. If it's not
+ // true, we can compare it against |mTaskBoundsAtDragStart|.
+ final int oldLeft = mHasMoved ? mRepositionTaskBounds.left : mTaskBoundsAtDragStart.left;
+ final int oldTop = mHasMoved ? mRepositionTaskBounds.top : mTaskBoundsAtDragStart.top;
+ final int oldRight = mHasMoved ? mRepositionTaskBounds.right : mTaskBoundsAtDragStart.right;
+ final int oldBottom =
+ mHasMoved ? mRepositionTaskBounds.bottom : mTaskBoundsAtDragStart.bottom;
- final float deltaX = x - mResizeStartPoint.x;
- final float deltaY = y - mResizeStartPoint.y;
- mResizeTaskBounds.set(mTaskBoundsAtDragStart);
+ final float deltaX = x - mRepositionStartPoint.x;
+ final float deltaY = y - mRepositionStartPoint.y;
+ mRepositionTaskBounds.set(mTaskBoundsAtDragStart);
if ((mCtrlType & CTRL_TYPE_LEFT) != 0) {
- mResizeTaskBounds.left += deltaX;
+ mRepositionTaskBounds.left += deltaX;
}
if ((mCtrlType & CTRL_TYPE_RIGHT) != 0) {
- mResizeTaskBounds.right += deltaX;
+ mRepositionTaskBounds.right += deltaX;
}
if ((mCtrlType & CTRL_TYPE_TOP) != 0) {
- mResizeTaskBounds.top += deltaY;
+ mRepositionTaskBounds.top += deltaY;
}
if ((mCtrlType & CTRL_TYPE_BOTTOM) != 0) {
- mResizeTaskBounds.bottom += deltaY;
+ mRepositionTaskBounds.bottom += deltaY;
}
if (mCtrlType == CTRL_TYPE_UNDEFINED) {
- mResizeTaskBounds.offset((int) deltaX, (int) deltaY);
+ mRepositionTaskBounds.offset((int) deltaX, (int) deltaY);
}
- if (oldLeft == mResizeTaskBounds.left && oldTop == mResizeTaskBounds.top
- && oldRight == mResizeTaskBounds.right && oldBottom == mResizeTaskBounds.bottom) {
+ if (oldLeft == mRepositionTaskBounds.left && oldTop == mRepositionTaskBounds.top
+ && oldRight == mRepositionTaskBounds.right
+ && oldBottom == mRepositionTaskBounds.bottom) {
return false;
}
- wct.setBounds(mWindowDecoration.mTaskInfo.token, mResizeTaskBounds);
+ wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
return true;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
index d5bb901..62bfd17 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
@@ -92,6 +92,7 @@
Context mContext;
TaskView mTaskView;
TaskViewTransitions mTaskViewTransitions;
+ TaskViewTaskController mTaskViewTaskController;
@Before
public void setUp() {
@@ -125,7 +126,9 @@
doReturn(true).when(mTransitions).isRegistered();
}
mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions));
- mTaskView = new TaskView(mContext, mOrganizer, mTaskViewTransitions, mSyncQueue);
+ mTaskViewTaskController = new TaskViewTaskController(mContext, mOrganizer,
+ mTaskViewTransitions, mSyncQueue);
+ mTaskView = new TaskView(mContext, mTaskViewTaskController);
mTaskView.setListener(mExecutor, mViewListener);
}
@@ -138,7 +141,8 @@
@Test
public void testSetPendingListener_throwsException() {
- TaskView taskView = new TaskView(mContext, mOrganizer, mTaskViewTransitions, mSyncQueue);
+ TaskView taskView = new TaskView(mContext,
+ new TaskViewTaskController(mContext, mOrganizer, mTaskViewTransitions, mSyncQueue));
taskView.setListener(mExecutor, mViewListener);
try {
taskView.setListener(mExecutor, mViewListener);
@@ -152,16 +156,17 @@
@Test
public void testStartActivity() {
ActivityOptions options = ActivityOptions.makeBasic();
- mTaskView.startActivity(mock(PendingIntent.class), null, options, new Rect(0, 0, 100, 100));
+ mTaskView.startActivity(mock(PendingIntent.class), null, options,
+ new Rect(0, 0, 100, 100));
- verify(mOrganizer).setPendingLaunchCookieListener(any(), eq(mTaskView));
+ verify(mOrganizer).setPendingLaunchCookieListener(any(), eq(mTaskViewTaskController));
assertThat(options.getLaunchWindowingMode()).isEqualTo(WINDOWING_MODE_MULTI_WINDOW);
}
@Test
public void testOnTaskAppeared_noSurface_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
verify(mViewListener, never()).onInitialized();
@@ -173,7 +178,7 @@
public void testOnTaskAppeared_withSurface_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
assertThat(mTaskView.isInitialized()).isTrue();
@@ -194,7 +199,7 @@
@Test
public void testSurfaceCreated_withTask_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
verify(mViewListener).onInitialized();
@@ -216,7 +221,7 @@
public void testSurfaceDestroyed_withTask_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
SurfaceHolder sh = mock(SurfaceHolder.class);
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.surfaceCreated(sh);
reset(mViewListener);
mTaskView.surfaceDestroyed(sh);
@@ -227,11 +232,11 @@
@Test
public void testOnReleased_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
mTaskView.release();
- verify(mOrganizer).removeListener(eq(mTaskView));
+ verify(mOrganizer).removeListener(eq(mTaskViewTaskController));
verify(mViewListener).onReleased();
assertThat(mTaskView.isInitialized()).isFalse();
}
@@ -239,9 +244,9 @@
@Test
public void testOnTaskVanished_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
- mTaskView.onTaskVanished(mTaskInfo);
+ mTaskViewTaskController.onTaskVanished(mTaskInfo);
verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId));
}
@@ -249,8 +254,8 @@
@Test
public void testOnBackPressedOnTaskRoot_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
- mTaskView.onBackPressedOnTaskRoot(mTaskInfo);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onBackPressedOnTaskRoot(mTaskInfo);
verify(mViewListener).onBackPressedOnTaskRoot(eq(mTaskInfo.taskId));
}
@@ -258,17 +263,17 @@
@Test
public void testSetOnBackPressedOnTaskRoot_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
}
@Test
public void testUnsetOnBackPressedOnTaskRoot_legacyTransitions() {
assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
- mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
- mTaskView.onTaskVanished(mTaskInfo);
+ mTaskViewTaskController.onTaskVanished(mTaskInfo);
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false));
}
@@ -276,8 +281,9 @@
public void testOnNewTask_noSurface() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
verify(mViewListener, never()).onInitialized();
@@ -303,8 +309,9 @@
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
@@ -314,15 +321,17 @@
public void testSurfaceCreated_withTask() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
verify(mViewListener).onInitialized();
- verify(mTaskViewTransitions).setTaskViewVisible(eq(mTaskView), eq(true));
+ verify(mTaskViewTransitions).setTaskViewVisible(eq(mTaskViewTaskController), eq(true));
- mTaskView.prepareOpenAnimation(false /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(false /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(true));
}
@@ -342,15 +351,16 @@
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
SurfaceHolder sh = mock(SurfaceHolder.class);
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
mTaskView.surfaceCreated(sh);
reset(mViewListener);
mTaskView.surfaceDestroyed(sh);
- verify(mTaskViewTransitions).setTaskViewVisible(eq(mTaskView), eq(false));
+ verify(mTaskViewTransitions).setTaskViewVisible(eq(mTaskViewTaskController), eq(false));
- mTaskView.prepareHideAnimation(new SurfaceControl.Transaction());
+ mTaskViewTaskController.prepareHideAnimation(new SurfaceControl.Transaction());
verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(false));
}
@@ -359,25 +369,27 @@
public void testOnReleased() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
mTaskView.release();
- verify(mOrganizer).removeListener(eq(mTaskView));
+ verify(mOrganizer).removeListener(eq(mTaskViewTaskController));
verify(mViewListener).onReleased();
assertThat(mTaskView.isInitialized()).isFalse();
- verify(mTaskViewTransitions).removeTaskView(eq(mTaskView));
+ verify(mTaskViewTransitions).removeTaskView(eq(mTaskViewTaskController));
}
@Test
public void testOnTaskVanished() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
- mTaskView.prepareCloseAnimation();
+ mTaskViewTaskController.prepareCloseAnimation();
verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId));
}
@@ -386,9 +398,10 @@
public void testOnBackPressedOnTaskRoot() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
- mTaskView.onBackPressedOnTaskRoot(mTaskInfo);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
+ mTaskViewTaskController.onBackPressedOnTaskRoot(mTaskInfo);
verify(mViewListener).onBackPressedOnTaskRoot(eq(mTaskInfo.taskId));
}
@@ -397,8 +410,9 @@
public void testSetOnBackPressedOnTaskRoot() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
}
@@ -406,11 +420,12 @@
public void testUnsetOnBackPressedOnTaskRoot() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
WindowContainerTransaction wct = new WindowContainerTransaction();
- mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
- new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
- mTaskView.prepareCloseAnimation();
+ mTaskViewTaskController.prepareCloseAnimation();
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false));
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
index 804c416..f185a8a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
@@ -66,13 +66,13 @@
@Test
fun testDragResize_notMove_skipsTransactionOnEnd() {
- taskPositioner.onDragResizeStart(
+ taskPositioner.onDragPositioningStart(
CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
- taskPositioner.onDragResizeEnd(
+ taskPositioner.onDragPositioningEnd(
STARTING_BOUNDS.left.toFloat() + 10,
STARTING_BOUNDS.top.toFloat() + 10
)
@@ -87,18 +87,18 @@
@Test
fun testDragResize_noEffectiveMove_skipsTransactionOnMoveAndEnd() {
- taskPositioner.onDragResizeStart(
+ taskPositioner.onDragPositioningStart(
CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
- taskPositioner.onDragResizeMove(
+ taskPositioner.onDragPositioningMove(
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
- taskPositioner.onDragResizeEnd(
+ taskPositioner.onDragPositioningEnd(
STARTING_BOUNDS.left.toFloat() + 10,
STARTING_BOUNDS.top.toFloat() + 10
)
@@ -113,13 +113,13 @@
@Test
fun testDragResize_hasEffectiveMove_issuesTransactionOnMoveAndEnd() {
- taskPositioner.onDragResizeStart(
+ taskPositioner.onDragPositioningStart(
CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
- taskPositioner.onDragResizeMove(
+ taskPositioner.onDragPositioningMove(
STARTING_BOUNDS.left.toFloat() + 10,
STARTING_BOUNDS.top.toFloat()
)
@@ -133,7 +133,7 @@
}
})
- taskPositioner.onDragResizeEnd(
+ taskPositioner.onDragPositioningEnd(
STARTING_BOUNDS.left.toFloat() + 10,
STARTING_BOUNDS.top.toFloat() + 10
)
@@ -150,7 +150,7 @@
@Test
fun testDragResize_move_skipsDragResizingFlag() {
- taskPositioner.onDragResizeStart(
+ taskPositioner.onDragPositioningStart(
CTRL_TYPE_UNDEFINED, // Move
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
@@ -159,12 +159,12 @@
// Move the task 10px to the right.
val newX = STARTING_BOUNDS.left.toFloat() + 10
val newY = STARTING_BOUNDS.top.toFloat()
- taskPositioner.onDragResizeMove(
+ taskPositioner.onDragPositioningMove(
newX,
newY
)
- taskPositioner.onDragResizeEnd(newX, newY)
+ taskPositioner.onDragPositioningEnd(newX, newY)
verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
@@ -177,7 +177,7 @@
@Test
fun testDragResize_resize_setsDragResizingFlag() {
- taskPositioner.onDragResizeStart(
+ taskPositioner.onDragPositioningStart(
CTRL_TYPE_RIGHT, // Resize right
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
@@ -186,12 +186,12 @@
// Resize the task by 10px to the right.
val newX = STARTING_BOUNDS.right.toFloat() + 10
val newY = STARTING_BOUNDS.top.toFloat()
- taskPositioner.onDragResizeMove(
+ taskPositioner.onDragPositioningMove(
newX,
newY
)
- taskPositioner.onDragResizeEnd(newX, newY)
+ taskPositioner.onDragPositioningEnd(newX, newY)
verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index b563627..13357fa 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -37,7 +37,6 @@
#include "SkShader.h"
#include "SkBlendMode.h"
#include "unicode/uloc.h"
-#include "unicode/ushape.h"
#include "utils/Blur.h"
#include <hwui/BlurDrawLooper.h>
diff --git a/location/java/android/location/GnssMeasurementsEvent.java b/location/java/android/location/GnssMeasurementsEvent.java
index d4b861f..4fc2ee8 100644
--- a/location/java/android/location/GnssMeasurementsEvent.java
+++ b/location/java/android/location/GnssMeasurementsEvent.java
@@ -43,7 +43,7 @@
private final List<GnssAutomaticGainControl> mGnssAgcs;
private final boolean mIsFullTracking;
- private static final int HAS_FULL_TRACKING = 1;
+ private static final int HAS_IS_FULL_TRACKING = 1;
/**
* Used for receiving GNSS satellite measurements from the GNSS engine.
@@ -174,7 +174,7 @@
* False indicates that the GNSS chipset may optimize power via duty cycling, constellations and
* frequency limits, etc.
*
- * <p>The value is only available if {@link #hasFullTracking()} is {@code true}.
+ * <p>The value is only available if {@link #hasIsFullTracking()} is {@code true}.
*/
public boolean isFullTracking() {
return mIsFullTracking;
@@ -183,8 +183,8 @@
/**
* Return {@code true} if {@link #isFullTracking()} is available, {@code false} otherwise.
*/
- public boolean hasFullTracking() {
- return (mFlag & HAS_FULL_TRACKING) == HAS_FULL_TRACKING;
+ public boolean hasIsFullTracking() {
+ return (mFlag & HAS_IS_FULL_TRACKING) == HAS_IS_FULL_TRACKING;
}
public static final @android.annotation.NonNull Creator<GnssMeasurementsEvent> CREATOR =
@@ -227,7 +227,7 @@
builder.append(mClock);
builder.append(' ').append(mMeasurements.toString());
builder.append(' ').append(mGnssAgcs.toString());
- if (hasFullTracking()) {
+ if (hasIsFullTracking()) {
builder.append(" isFullTracking=").append(mIsFullTracking);
}
builder.append("]");
@@ -334,8 +334,8 @@
* and frequency limits, etc.
*/
@NonNull
- public Builder setFullTracking(boolean isFullTracking) {
- mFlag |= HAS_FULL_TRACKING;
+ public Builder setIsFullTracking(boolean isFullTracking) {
+ mFlag |= HAS_IS_FULL_TRACKING;
mIsFullTracking = isFullTracking;
return this;
}
@@ -344,8 +344,8 @@
* Clears the full tracking mode indicator.
*/
@NonNull
- public Builder clearFullTracking() {
- mFlag &= ~HAS_FULL_TRACKING;
+ public Builder clearIsFullTracking() {
+ mFlag &= ~HAS_IS_FULL_TRACKING;
return this;
}
diff --git a/media/java/android/media/AudioPresentation.aidl b/media/java/android/media/AudioPresentation.aidl
new file mode 100644
index 0000000..3fb5a5b
--- /dev/null
+++ b/media/java/android/media/AudioPresentation.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+parcelable AudioPresentation;
diff --git a/media/java/android/media/AudioPresentation.java b/media/java/android/media/AudioPresentation.java
index 05f3c5a..a413545 100644
--- a/media/java/android/media/AudioPresentation.java
+++ b/media/java/android/media/AudioPresentation.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -19,6 +19,9 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.icu.util.ULocale;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -49,7 +52,7 @@
* Applications that parse media streams and extract presentation information on their own
* can create instances of AudioPresentation by using {@link AudioPresentation.Builder} class.
*/
-public final class AudioPresentation {
+public final class AudioPresentation implements Parcelable {
private final int mPresentationId;
private final int mProgramId;
private final ULocale mLanguage;
@@ -126,7 +129,7 @@
private final boolean mAudioDescriptionAvailable;
private final boolean mSpokenSubtitlesAvailable;
private final boolean mDialogueEnhancementAvailable;
- private final Map<ULocale, CharSequence> mLabels;
+ private final HashMap<ULocale, String> mLabels;
/**
* No preferred reproduction channel layout.
@@ -160,9 +163,14 @@
public static final int MASTERED_FOR_HEADPHONE = 4;
/**
- * This ID is reserved. No items can be explicitly assigned this ID.
+ * Unknown audio presentation ID, this indicates audio presentation ID is not selected.
*/
- private static final int UNKNOWN_ID = -1;
+ public static final int PRESENTATION_ID_UNKNOWN = -1;
+
+ /**
+ * Unknown audio program ID, this indicates audio program ID is not selected.
+ */
+ public static final int PROGRAM_ID_UNKNOWN = -1;
/**
* This allows an application developer to construct an AudioPresentation object with all the
@@ -191,7 +199,7 @@
boolean audioDescriptionAvailable,
boolean spokenSubtitlesAvailable,
boolean dialogueEnhancementAvailable,
- @NonNull Map<ULocale, CharSequence> labels) {
+ @NonNull Map<ULocale, String> labels) {
mPresentationId = presentationId;
mProgramId = programId;
mLanguage = language;
@@ -199,7 +207,18 @@
mAudioDescriptionAvailable = audioDescriptionAvailable;
mSpokenSubtitlesAvailable = spokenSubtitlesAvailable;
mDialogueEnhancementAvailable = dialogueEnhancementAvailable;
- mLabels = new HashMap<ULocale, CharSequence>(labels);
+ mLabels = new HashMap<ULocale, String>(labels);
+ }
+
+ private AudioPresentation(@NonNull Parcel in) {
+ mPresentationId = in.readInt();
+ mProgramId = in.readInt();
+ mLanguage = in.readSerializable(ULocale.class.getClassLoader(), ULocale.class);
+ mMasteringIndication = in.readInt();
+ mAudioDescriptionAvailable = in.readBoolean();
+ mSpokenSubtitlesAvailable = in.readBoolean();
+ mDialogueEnhancementAvailable = in.readBoolean();
+ mLabels = in.readSerializable(HashMap.class.getClassLoader(), HashMap.class);
}
/**
@@ -225,13 +244,13 @@
*/
public Map<Locale, String> getLabels() {
Map<Locale, String> localeLabels = new HashMap<Locale, String>(mLabels.size());
- for (Map.Entry<ULocale, CharSequence> entry : mLabels.entrySet()) {
- localeLabels.put(entry.getKey().toLocale(), entry.getValue().toString());
+ for (Map.Entry<ULocale, String> entry : mLabels.entrySet()) {
+ localeLabels.put(entry.getKey().toLocale(), entry.getValue());
}
return localeLabels;
}
- private Map<ULocale, CharSequence> getULabels() {
+ private Map<ULocale, String> getULabels() {
return mLabels;
}
@@ -335,13 +354,13 @@
*/
public static final class Builder {
private final int mPresentationId;
- private int mProgramId = UNKNOWN_ID;
+ private int mProgramId = PROGRAM_ID_UNKNOWN;
private ULocale mLanguage = new ULocale("");
private int mMasteringIndication = MASTERING_NOT_INDICATED;
private boolean mAudioDescriptionAvailable = false;
private boolean mSpokenSubtitlesAvailable = false;
private boolean mDialogueEnhancementAvailable = false;
- private Map<ULocale, CharSequence> mLabels = new HashMap<ULocale, CharSequence>();
+ private HashMap<ULocale, String> mLabels = new HashMap<ULocale, String>();
/**
* Create a {@link Builder}. Any field that should be included in the
@@ -402,7 +421,10 @@
* @param labels Text label indexed by its locale corresponding to the language code.
*/
public @NonNull Builder setLabels(@NonNull Map<ULocale, CharSequence> labels) {
- mLabels = new HashMap<ULocale, CharSequence>(labels);
+ mLabels.clear();
+ for (Map.Entry<ULocale, CharSequence> entry : labels.entrySet()) {
+ mLabels.put(entry.getKey(), entry.getValue().toString());
+ }
return this;
}
@@ -448,4 +470,35 @@
mDialogueEnhancementAvailable, mLabels);
}
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(getPresentationId());
+ dest.writeInt(getProgramId());
+ dest.writeSerializable(getULocale());
+ dest.writeInt(getMasteringIndication());
+ dest.writeBoolean(hasAudioDescription());
+ dest.writeBoolean(hasSpokenSubtitles());
+ dest.writeBoolean(hasDialogueEnhancement());
+ dest.writeSerializable(mLabels);
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<AudioPresentation> CREATOR =
+ new Parcelable.Creator<AudioPresentation>() {
+ @Override
+ public AudioPresentation createFromParcel(@NonNull Parcel in) {
+ return new AudioPresentation(in);
+ }
+
+ @Override
+ public AudioPresentation[] newArray(int size) {
+ return new AudioPresentation[size];
+ }
+ };
}
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index d55d287..c4f60c3 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -17,6 +17,7 @@
package android.media.tv;
import android.content.ComponentName;
+import android.media.AudioPresentation;
import android.media.tv.AdBuffer;
import android.media.tv.AdResponse;
import android.media.tv.AitInfo;
@@ -37,6 +38,8 @@
void onSessionReleased(int seq);
void onSessionEvent(in String name, in Bundle args, int seq);
void onChannelRetuned(in Uri channelUri, int seq);
+ void onAudioPresentationsChanged(in List<AudioPresentation> AudioPresentations, int seq);
+ void onAudioPresentationSelected(int presentationId, int programId, int seq);
void onTracksChanged(in List<TvTrackInfo> tracks, int seq);
void onTrackSelected(int type, in String trackId, int seq);
void onVideoAvailable(int seq);
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index f7c1e3c..746cfa2 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -16,9 +16,11 @@
package android.media.tv;
+import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Rect;
+import android.media.AudioPresentation;
import android.media.PlaybackParams;
import android.media.tv.AdBuffer;
import android.media.tv.AdRequest;
@@ -63,7 +65,7 @@
void addBlockedRating(in String rating, int userId);
void removeBlockedRating(in String rating, int userId);
- void createSession(in ITvInputClient client, in String inputId, boolean isRecordingSession,
+ void createSession(in ITvInputClient client, in String inputId, in AttributionSource tvAppAttributionSource, boolean isRecordingSession,
int seq, int userId);
void releaseSession(in IBinder sessionToken, int userId);
int getClientPid(in String sessionId);
@@ -77,6 +79,8 @@
void tune(in IBinder sessionToken, in Uri channelUri, in Bundle params, int userId);
void setCaptionEnabled(in IBinder sessionToken, boolean enabled, int userId);
void selectTrack(in IBinder sessionToken, int type, in String trackId, int userId);
+ void selectAudioPresentation(in IBinder sessionToken, int presentationId, int programId,
+ int userId);
void setInteractiveAppNotificationEnabled(in IBinder sessionToken, boolean enabled, int userId);
diff --git a/media/java/android/media/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl
index 64a23a2..be73c0c 100755
--- a/media/java/android/media/tv/ITvInputService.aidl
+++ b/media/java/android/media/tv/ITvInputService.aidl
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.content.AttributionSource;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.media.tv.ITvInputServiceCallback;
import android.media.tv.ITvInputSessionCallback;
@@ -30,7 +31,7 @@
oneway void registerCallback(in ITvInputServiceCallback callback);
oneway void unregisterCallback(in ITvInputServiceCallback callback);
oneway void createSession(in InputChannel channel, in ITvInputSessionCallback callback,
- in String inputId, in String sessionId);
+ in String inputId, in String sessionId, in AttributionSource tvAppAttributionSource);
oneway void createRecordingSession(in ITvInputSessionCallback callback, in String inputId,
in String sessionId);
List<String> getAvailableExtensionInterfaceNames();
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 326b98d..e6c09a9 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -17,6 +17,7 @@
package android.media.tv;
import android.graphics.Rect;
+import android.media.AudioPresentation;
import android.media.PlaybackParams;
import android.media.tv.AdBuffer;
import android.media.tv.AdRequest;
@@ -41,6 +42,7 @@
void setVolume(float volume);
void tune(in Uri channelUri, in Bundle params);
void setCaptionEnabled(boolean enabled);
+ void selectAudioPresentation(int presentationId, int programId);
void selectTrack(int type, in String trackId);
void setInteractiveAppNotificationEnabled(boolean enable);
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 8216622..0111f09 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.media.AudioPresentation;
import android.media.tv.AdBuffer;
import android.media.tv.AdResponse;
import android.media.tv.AitInfo;
@@ -34,6 +35,8 @@
void onSessionCreated(ITvInputSession session, in IBinder hardwareSessionToken);
void onSessionEvent(in String name, in Bundle args);
void onChannelRetuned(in Uri channelUri);
+ void onAudioPresentationsChanged(in List<AudioPresentation> tvAudioPresentations);
+ void onAudioPresentationSelected(int presentationId, int programId);
void onTracksChanged(in List<TvTrackInfo> tracks);
void onTrackSelected(int type, in String trackId);
void onVideoAvailable();
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 634f102..847762f 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -30,7 +30,6 @@
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.Surface;
-
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
@@ -75,6 +74,7 @@
private static final int DO_SET_IAPP_NOTIFICATION_ENABLED = 26;
private static final int DO_REQUEST_AD = 27;
private static final int DO_NOTIFY_AD_BUFFER = 28;
+ private static final int DO_SELECT_AUDIO_PRESENTATION = 29;
private final boolean mIsRecordingSession;
private final HandlerCaller mCaller;
@@ -240,6 +240,12 @@
mTvInputRecordingSessionImpl.resumeRecording((Bundle) msg.obj);
break;
}
+ case DO_SELECT_AUDIO_PRESENTATION: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mTvInputSessionImpl.selectAudioPresentation(args.argi1, args.argi2);
+ args.recycle();
+ break;
+ }
case DO_REQUEST_BROADCAST_INFO: {
mTvInputSessionImpl.requestBroadcastInfo((BroadcastInfoRequest) msg.obj);
break;
@@ -275,8 +281,8 @@
+ "Consider handling the tune request in a separate thread.");
}
if (durationMs > EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS) {
- throw new RuntimeException("Too much time to handle a request. (type=" + msg.what +
- ", " + durationMs + "ms > " + EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS + "ms).");
+ throw new RuntimeException("Too much time to handle a request. (type=" + msg.what
+ + ", " + durationMs + "ms > " + EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS + "ms).");
}
}
}
@@ -323,6 +329,12 @@
}
@Override
+ public void selectAudioPresentation(int presentationId, int programId) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageII(DO_SELECT_AUDIO_PRESENTATION,
+ presentationId, programId));
+ }
+
+ @Override
public void selectTrack(int type, String trackId) {
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_SELECT_TRACK, type, trackId));
}
@@ -436,7 +448,7 @@
}
private final class TvInputEventReceiver extends InputEventReceiver {
- public TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index e8127df..a4e2212 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -26,11 +26,13 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat.Encoding;
+import android.media.AudioPresentation;
import android.media.PlaybackParams;
import android.media.tv.interactive.TvInteractiveAppManager;
import android.net.Uri;
@@ -54,9 +56,7 @@
import android.view.KeyEvent;
import android.view.Surface;
import android.view.View;
-
import com.android.internal.util.Preconditions;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -550,6 +550,27 @@
}
/**
+ * This is called when the audio presentation information of the session has been changed.
+ *
+ * @param session A {@link TvInputManager.Session} associated with this callback.
+ * @param audioPresentations An updated list of selectable audio presentations.
+ */
+ public void onAudioPresentationsChanged(Session session,
+ List<AudioPresentation> audioPresentations) {
+ }
+
+ /**
+ * This is called when an audio presentation is selected.
+ *
+ * @param session A {@link TvInputManager.Session} associated with this callback.
+ * @param presentationId The ID of the selected audio presentation.
+ * @param programId The ID of the program providing the selected audio presentation.
+ */
+ public void onAudioPresentationSelected(Session session, int presentationId,
+ int programId) {
+ }
+
+ /**
* This is called when the track information of the session has been changed.
*
* @param session A {@link TvInputManager.Session} associated with this callback.
@@ -777,6 +798,25 @@
});
}
+ void postAudioPresentationsChanged(final List<AudioPresentation> audioPresentations) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onAudioPresentationsChanged(mSession, audioPresentations);
+ }
+ });
+ }
+
+ void postAudioPresentationSelected(final int presentationId, final int programId) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onAudioPresentationSelected(mSession, presentationId,
+ programId);
+ }
+ });
+ }
+
void postTracksChanged(final List<TvTrackInfo> tracks) {
mHandler.post(new Runnable() {
@Override
@@ -1234,6 +1274,36 @@
record.postChannelRetuned(channelUri);
}
}
+ @Override
+ public void onAudioPresentationsChanged(List<AudioPresentation> audioPresentations,
+ int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ if (record.mSession.updateAudioPresentations(audioPresentations)) {
+ record.postAudioPresentationsChanged(audioPresentations);
+ }
+ }
+ }
+
+ @Override
+ public void onAudioPresentationSelected(int presentationId, int programId, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ if (record.mSession.updateAudioPresentationSelection(presentationId,
+ programId)) {
+ record.postAudioPresentationSelected(presentationId, programId);
+ }
+ }
+ }
+
@Override
public void onTracksChanged(List<TvTrackInfo> tracks, int seq) {
@@ -1910,13 +1980,15 @@
* of the given TV input.
*
* @param inputId The ID of the TV input.
+ * @param tvAppAttributionSource The Attribution Source of the TV App.
* @param callback A callback used to receive the created session.
* @param handler A {@link Handler} that the session creation will be delivered to.
* @hide
*/
- public void createSession(@NonNull String inputId, @NonNull final SessionCallback callback,
- @NonNull Handler handler) {
- createSessionInternal(inputId, false, callback, handler);
+ public void createSession(@NonNull String inputId,
+ @NonNull AttributionSource tvAppAttributionSource,
+ @NonNull final SessionCallback callback, @NonNull Handler handler) {
+ createSessionInternal(inputId, tvAppAttributionSource, false, callback, handler);
}
/**
@@ -1941,7 +2013,7 @@
* @param useCase the use case type of the client.
* {@see TvInputService#PriorityHintUseCaseType}.
* @param sessionId the unique id of the session owned by the client.
- * {@see TvInputService#onCreateSession(String, String)}.
+ * {@see TvInputService#onCreateSession(String, String, AttributionSource)}.
*
* @return the use case priority value for the given use case type and the client's foreground
* or background status.
@@ -1992,11 +2064,11 @@
*/
public void createRecordingSession(@NonNull String inputId,
@NonNull final SessionCallback callback, @NonNull Handler handler) {
- createSessionInternal(inputId, true, callback, handler);
+ createSessionInternal(inputId, null, true, callback, handler);
}
- private void createSessionInternal(String inputId, boolean isRecordingSession,
- SessionCallback callback, Handler handler) {
+ private void createSessionInternal(String inputId, AttributionSource tvAppAttributionSource,
+ boolean isRecordingSession, SessionCallback callback, Handler handler) {
Preconditions.checkNotNull(inputId);
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(handler);
@@ -2005,7 +2077,8 @@
int seq = mNextSeq++;
mSessionCallbackRecordMap.put(seq, record);
try {
- mService.createSession(mClient, inputId, isRecordingSession, seq, mUserId);
+ mService.createSession(
+ mClient, inputId, tvAppAttributionSource, isRecordingSession, seq, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2176,8 +2249,8 @@
* @param deviceId The device ID to acquire Hardware for.
* @param info The TV input which will use the acquired Hardware.
* @param tvInputSessionId a String returned to TIS when the session was created.
- * {@see TvInputService#onCreateSession(String, String)}. If null, the client will be
- * treated as a background app.
+ * {@see TvInputService#onCreateSession(String, String, AttributionSource)}. If null, the
+ * client will be treated as a background app.
* @param priorityHint The use case of the client. {@see TvInputService#PriorityHintUseCaseType}
* @param executor the executor on which the listener would be invoked.
* @param callback A callback to receive updates on Hardware.
@@ -2399,12 +2472,18 @@
private final Object mMetadataLock = new Object();
// @GuardedBy("mMetadataLock")
+ private final List<AudioPresentation> mAudioPresentations = new ArrayList<>();
+ // @GuardedBy("mMetadataLock")
private final List<TvTrackInfo> mAudioTracks = new ArrayList<>();
// @GuardedBy("mMetadataLock")
private final List<TvTrackInfo> mVideoTracks = new ArrayList<>();
// @GuardedBy("mMetadataLock")
private final List<TvTrackInfo> mSubtitleTracks = new ArrayList<>();
// @GuardedBy("mMetadataLock")
+ private int mSelectedAudioProgramId = AudioPresentation.PROGRAM_ID_UNKNOWN;
+ // @GuardedBy("mMetadataLock")
+ private int mSelectedAudioPresentationId = AudioPresentation.PRESENTATION_ID_UNKNOWN;
+ // @GuardedBy("mMetadataLock")
private String mSelectedAudioTrackId;
// @GuardedBy("mMetadataLock")
private String mSelectedVideoTrackId;
@@ -2552,9 +2631,12 @@
return;
}
synchronized (mMetadataLock) {
+ mAudioPresentations.clear();
mAudioTracks.clear();
mVideoTracks.clear();
mSubtitleTracks.clear();
+ mSelectedAudioProgramId = AudioPresentation.PROGRAM_ID_UNKNOWN;
+ mSelectedAudioPresentationId = AudioPresentation.PRESENTATION_ID_UNKNOWN;
mSelectedAudioTrackId = null;
mSelectedVideoTrackId = null;
mSelectedSubtitleTrackId = null;
@@ -2586,6 +2668,119 @@
}
/**
+ * Selects an audio presentation
+ *
+ * @param presentationId The ID of the audio presentation to select.
+ * @param programId The ID of the program offering the selected audio presentation.
+ * @see #getAudioPresentations
+ */
+ public void selectAudioPresentation(int presentationId, int programId) {
+ synchronized (mMetadataLock) {
+ if (presentationId != AudioPresentation.PRESENTATION_ID_UNKNOWN
+ && !containsAudioPresentation(mAudioPresentations, presentationId)) {
+ Log.w(TAG, "Invalid audio presentation id: " + presentationId);
+ return;
+ }
+ }
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.selectAudioPresentation(mToken, presentationId, programId, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private boolean containsAudioPresentation(List<AudioPresentation> audioPresentations,
+ int presentationId) {
+ synchronized (mMetadataLock) {
+ for (AudioPresentation audioPresentation : audioPresentations) {
+ if (audioPresentation.getPresentationId() == presentationId) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Returns a list of audio presentations.
+ *
+ * @return the list of audio presentations.
+ * Returns empty AudioPresentation list if no presentations are available.
+ */
+ public List<AudioPresentation> getAudioPresentations() {
+ synchronized (mMetadataLock) {
+ if (mAudioPresentations == null) {
+ return new ArrayList<AudioPresentation>();
+ }
+ return new ArrayList<AudioPresentation>(mAudioPresentations);
+ }
+ }
+
+ /**
+ * Returns the program ID of the selected audio presentation.
+ *
+ * @return The ID of the program providing the selected audio presentation.
+ * Returns {@value AudioPresentation.PROGRAM_ID_UNKNOWN} if no audio presentation has
+ * been selected from a program.
+ * @see #selectAudioPresentation
+ */
+ public int getSelectedProgramId() {
+ synchronized (mMetadataLock) {
+ return mSelectedAudioProgramId;
+ }
+ }
+
+ /**
+ * Returns the presentation ID of the selected audio presentation.
+ *
+ * @return The ID of the selected audio presentation.
+ * Returns {@value AudioPresentation.PRESENTATION_ID_UNKNOWN} if no audio presentation
+ * has been selected.
+ * @see #selectAudioPresentation
+ */
+ public int getSelectedAudioPresentationId() {
+ synchronized (mMetadataLock) {
+ return mSelectedAudioPresentationId;
+ }
+ }
+
+ /**
+ * Responds to onAudioPresentationsChanged() and updates the internal audio presentation
+ * information.
+ * @return true if there is an update.
+ */
+ boolean updateAudioPresentations(List<AudioPresentation> audioPresentations) {
+ synchronized (mMetadataLock) {
+ mAudioPresentations.clear();
+ for (AudioPresentation presentation : audioPresentations) {
+ mAudioPresentations.add(presentation);
+ }
+ return !mAudioPresentations.isEmpty();
+ }
+ }
+
+ /**
+ * Responds to onAudioPresentationSelected() and updates the internal audio presentation
+ * selection information.
+ * @return true if there is an update.
+ */
+ boolean updateAudioPresentationSelection(int presentationId, int programId) {
+ synchronized (mMetadataLock) {
+ if ((programId != mSelectedAudioProgramId)
+ || (presentationId != mSelectedAudioPresentationId)) {
+ mSelectedAudioPresentationId = presentationId;
+ mSelectedAudioProgramId = programId;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Selects a track.
*
* @param type The type of the track to select. The type can be
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
old mode 100755
new mode 100644
index 15f511b..b769757
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -26,11 +26,13 @@
import android.app.ActivityManager;
import android.app.Service;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.hdmi.HdmiDeviceInfo;
+import android.media.AudioPresentation;
import android.media.PlaybackParams;
import android.net.Uri;
import android.os.AsyncTask;
@@ -57,10 +59,8 @@
import android.view.WindowManager;
import android.view.accessibility.CaptioningManager;
import android.widget.FrameLayout;
-
import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -171,7 +171,7 @@
@Override
public void createSession(InputChannel channel, ITvInputSessionCallback cb,
- String inputId, String sessionId) {
+ String inputId, String sessionId, AttributionSource tvAppAttributionSource) {
if (channel == null) {
Log.w(TAG, "Creating session without input channel");
}
@@ -183,6 +183,7 @@
args.arg2 = cb;
args.arg3 = inputId;
args.arg4 = sessionId;
+ args.arg5 = tvAppAttributionSource;
mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION,
args).sendToTarget();
}
@@ -370,6 +371,24 @@
}
/**
+ * Returns a concrete implementation of {@link Session}.
+ *
+ * <p>For any apps that needs sessionId to request tuner resources from TunerResourceManager and
+ * needs to specify custom AttributionSource to AudioTrack, it needs to override this method to
+ * get the sessionId and AttrubutionSource passed. When no overriding, this method calls {@link
+ * #onCreateSession(String, String)} defaultly.
+ *
+ * @param inputId The ID of the TV input associated with the session.
+ * @param sessionId the unique sessionId created by TIF when session is created.
+ * @param tvAppAttributionSource The Attribution Source of the TV App.
+ */
+ @Nullable
+ public Session onCreateSession(@NonNull String inputId, @NonNull String sessionId,
+ @NonNull AttributionSource tvAppAttributionSource) {
+ return onCreateSession(inputId, sessionId);
+ }
+
+ /**
* Returns a concrete implementation of {@link RecordingSession}.
*
* <p>For any apps that needs sessionId to request tuner resources from TunerResourceManager,
@@ -739,6 +758,74 @@
}
/**
+ * Sends an updated list of all audio presentations available from a Next Generation Audio
+ * service. This is used by the framework to maintain the audio presentation information for
+ * a given track of {@link TvTrackInfo#TYPE_AUDIO}, which in turn is used by
+ * {@link TvView#getAudioPresentations} for the application to retrieve metadata for the
+ * current audio track. The TV input service must call this method as soon as the audio
+ * track presentation information becomes available or is updated. Note that in a case
+ * where a part of the information for the current track is updated, it is not necessary
+ * to create a new {@link TvTrackInfo} object with a different track ID.
+ *
+ * @param audioPresentations A list of audio presentation information pertaining to the
+ * selected track.
+ */
+ public void notifyAudioPresentationChanged(@NonNull final List<AudioPresentation>
+ audioPresentations) {
+ final List<AudioPresentation> ap = new ArrayList<>(audioPresentations);
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "notifyAudioPresentationsChanged");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onAudioPresentationsChanged(ap);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in notifyAudioPresentationsChanged", e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Sends the presentation and program IDs of the selected audio presentation. This is used
+ * to inform the application that a specific audio presentation is selected. The TV input
+ * service must call this method as soon as an audio presentation is selected either by
+ * default or in response to a call to {@link #onSelectTrack}. The selected audio
+ * presentation ID for a currently selected audio track is maintained in the framework until
+ * the next call to this method even after the entire audio presentation list for the track
+ * is updated (but is reset when the session is tuned to a new channel), so care must be
+ * taken not to result in an obsolete track audio presentation ID.
+ *
+ * @param presentationId The ID of the selected audio presentation for the current track.
+ * @param programId The ID of the program providing the selected audio presentation.
+ * @see #onSelectAudioPresentation
+ */
+ public void notifyAudioPresentationSelected(final int presentationId, final int programId) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "notifyAudioPresentationSelected");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onAudioPresentationSelected(presentationId, programId);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in notifyAudioPresentationSelected", e);
+ }
+ }
+ });
+ }
+
+
+ /**
* Informs the application that the user is allowed to watch the current program content.
*
* <p>Each TV input service is required to query the system whether the user is allowed to
@@ -1128,7 +1215,7 @@
public abstract void onSetStreamVolume(@FloatRange(from = 0.0, to = 1.0) float volume);
/**
- * called when broadcast info is requested.
+ * Called when broadcast info is requested.
*
* @param request broadcast info request
*/
@@ -1136,7 +1223,7 @@
}
/**
- * called when broadcast info is removed.
+ * Called when broadcast info is removed.
*/
public void onRemoveBroadcastInfo(int requestId) {
}
@@ -1246,6 +1333,23 @@
}
/**
+ * Selects an audio presentation.
+ *
+ * <p>On successfully selecting the audio presentation,
+ * {@link #notifyAudioPresentationSelected} is invoked to provide updated information about
+ * the selected audio presentation to applications.
+ *
+ * @param presentationId The ID of the audio presentation to select.
+ * @param programId The ID of the program providing the selected audio presentation.
+ * @return {@code true} if the audio presentation selection was successful,
+ * {@code false} otherwise.
+ * @see #notifyAudioPresentationSelected
+ */
+ public boolean onSelectAudioPresentation(int presentationId, int programId) {
+ return false;
+ }
+
+ /**
* Processes a private command sent from the application to the TV input. This can be used
* to provide domain-specific features that are only known between certain TV inputs and
* their clients.
@@ -1579,6 +1683,13 @@
}
/**
+ * Calls {@link #onSelectAudioPresentation}.
+ */
+ void selectAudioPresentation(int presentationId, int programId) {
+ onSelectAudioPresentation(presentationId, programId);
+ }
+
+ /**
* Calls {@link #onSelectTrack}.
*/
void selectTrack(int type, String trackId) {
@@ -2497,8 +2608,10 @@
ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2;
String inputId = (String) args.arg3;
String sessionId = (String) args.arg4;
+ AttributionSource tvAppAttributionSource = (AttributionSource) args.arg5;
args.recycle();
- Session sessionImpl = onCreateSession(inputId, sessionId);
+ Session sessionImpl =
+ onCreateSession(inputId, sessionId, tvAppAttributionSource);
if (sessionImpl == null) {
try {
// Failed to create a session.
@@ -2534,7 +2647,7 @@
proxySession.mServiceHandler = mServiceHandler;
TvInputManager manager = (TvInputManager) getSystemService(
Context.TV_INPUT_SERVICE);
- manager.createSession(hardwareInputId,
+ manager.createSession(hardwareInputId, tvAppAttributionSource,
proxySession.mHardwareSessionCallback, mServiceHandler);
} else {
SomeArgs someArgs = SomeArgs.obtain();
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 2fdbc3b..c7a63ac 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -21,6 +21,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -30,6 +31,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.media.AudioPresentation;
import android.media.PlaybackParams;
import android.media.tv.TvInputManager.Session;
import android.media.tv.TvInputManager.Session.FinishedInputEventCallback;
@@ -51,9 +53,9 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
-
import java.lang.ref.WeakReference;
import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
@@ -111,6 +113,7 @@
private int mSurfaceViewTop;
private int mSurfaceViewBottom;
private TimeShiftPositionCallback mTimeShiftPositionCallback;
+ private AttributionSource mTvAppAttributionSource;
private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
@Override
@@ -185,6 +188,7 @@
mDefStyleAttr = defStyleAttr;
resetSurfaceView();
mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
+ mTvAppAttributionSource = getContext().getAttributionSource();
}
/**
@@ -304,6 +308,22 @@
}
/**
+ * Override default attribution source of TV App.
+ *
+ * <p>An attribution source of TV App is used to attribute work to TV Input Service.
+ * The default attribution source is created by {@link Context#getAttributionSource()}.
+ * Call this method before calling {@link #tune(String, Uri, Bundle)} or {@link
+ * #timeShiftPlay(String, Uri)} to override the default attribution source.
+ *
+ * @param tvAppAttributionSource The attribution source of the TV App.
+ */
+ public void overrideTvAppAttributionSource(@NonNull AttributionSource tvAppAttributionSource) {
+ if (tvAppAttributionSource != null) {
+ mTvAppAttributionSource = tvAppAttributionSource;
+ }
+ }
+
+ /**
* Tunes to a given channel.
*
* @param inputId The ID of the TV input for the given channel.
@@ -355,7 +375,8 @@
// is obsolete and should ignore it.
mSessionCallback = new MySessionCallback(inputId, channelUri, params);
if (mTvInputManager != null) {
- mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
+ mTvInputManager.createSession(
+ inputId, mTvAppAttributionSource, mSessionCallback, mHandler);
}
}
}
@@ -434,6 +455,35 @@
}
/**
+ * Selects an audio presentation.
+ *
+ * @param presentationId The ID of the audio presentation to select.
+ * @param programId The ID of the program providing the selected audio presentation.
+ * @see #getAudioPresentations
+ */
+ public void selectAudioPresentation(int presentationId, int programId) {
+ if (mSession != null) {
+ mSession.selectAudioPresentation(presentationId, programId);
+ }
+ }
+
+ /**
+ * Returns the list of audio presentations from the selected track of type
+ * {@link TvTrackInfo#TYPE_AUDIO}.
+ *
+ * @return the list of audio presentations from the selected audio track, or an empty list if no
+ * audio presentations are available.
+ * @see #selectAudioPresentation
+ */
+ @NonNull
+ public List<AudioPresentation> getAudioPresentations() {
+ if (mSession == null) {
+ return new ArrayList<AudioPresentation>();
+ }
+ return mSession.getAudioPresentations();
+ }
+
+ /**
* Selects a track.
*
* @param type The type of the track to select. The type can be {@link TvTrackInfo#TYPE_AUDIO},
@@ -526,7 +576,8 @@
resetInternal();
mSessionCallback = new MySessionCallback(inputId, recordedProgramUri);
if (mTvInputManager != null) {
- mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
+ mTvInputManager.createSession(
+ inputId, mTvAppAttributionSource, mSessionCallback, mHandler);
}
}
}
@@ -977,6 +1028,27 @@
}
/**
+ * This is called when the audio presentation information has been changed.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param audioPresentations A list of updated audio presentation information.
+ */
+ public void onAudioPresentationsChanged(@NonNull String inputId,
+ @NonNull List<AudioPresentation> audioPresentations) {
+ }
+
+ /**
+ * This is called when audio presentation selection has changed.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param presentationId The ID of the audio presentation selected.
+ * @param programId The ID of the program providing the selected audio presentation.
+ */
+ public void onAudioPresentationSelected(@NonNull String inputId, int presentationId,
+ int programId) {
+ }
+
+ /**
* This is called when the track information has been changed.
*
* @param inputId The ID of the TV input bound to this view.
@@ -1242,6 +1314,37 @@
}
@Override
+ public void onAudioPresentationsChanged(Session session,
+ List<AudioPresentation> audioPresentations) {
+ if (DEBUG) {
+ Log.d(TAG, "onAudioPresentationsChanged(" + audioPresentations + ")");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onAudioPresentationsChanged - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onAudioPresentationsChanged(mInputId, audioPresentations);
+ }
+ }
+
+ @Override
+ public void onAudioPresentationSelected(Session session, int presentationId,
+ int programId) {
+ if (DEBUG) {
+ Log.d(TAG, "onAudioPresentationSelected(presentationId=" + presentationId
+ + ", programId=" + programId + ")");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onAudioPresentationSelected - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onAudioPresentationSelected(mInputId, presentationId, programId);
+ }
+ }
+
+ @Override
public void onTracksChanged(Session session, List<TvTrackInfo> tracks) {
if (DEBUG) {
Log.d(TAG, "onTracksChanged(" + tracks + ")");
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index ac920d2..7d08b81 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -1297,6 +1297,13 @@
return RESULT_UNAVAILABLE;
}
}
+ if (mFrontendType == FrontendSettings.TYPE_IPTV) {
+ if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_3_0,
+ "Tuner with IPTV Frontend")) {
+ return RESULT_UNAVAILABLE;
+ }
+ }
if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND,
mFrontendLock)) {
mScanCallback = scanCallback;
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 35ee3ee9..625e842 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -132,6 +132,11 @@
#include <aidl/android/hardware/tv/tuner/FrontendIsdbtPartialReceptionFlag.h>
#include <aidl/android/hardware/tv/tuner/FrontendIsdbtSettings.h>
#include <aidl/android/hardware/tv/tuner/FrontendIsdbtTimeInterleaveMode.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIptvSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIptvSettingsFec.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIptvSettingsProtocol.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIptvSettingsIgmp.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIptvSettingsFecType.h>
#include <aidl/android/hardware/tv/tuner/FrontendModulation.h>
#include <aidl/android/hardware/tv/tuner/FrontendModulationStatus.h>
#include <aidl/android/hardware/tv/tuner/FrontendRollOff.h>
@@ -283,6 +288,11 @@
using ::aidl::android::hardware::tv::tuner::FrontendIsdbtPartialReceptionFlag;
using ::aidl::android::hardware::tv::tuner::FrontendIsdbtSettings;
using ::aidl::android::hardware::tv::tuner::FrontendIsdbtTimeInterleaveMode;
+using ::aidl::android::hardware::tv::tuner::FrontendIptvSettings;
+using ::aidl::android::hardware::tv::tuner::FrontendIptvSettingsFec;
+using ::aidl::android::hardware::tv::tuner::FrontendIptvSettingsProtocol;
+using ::aidl::android::hardware::tv::tuner::FrontendIptvSettingsIgmp;
+using ::aidl::android::hardware::tv::tuner::FrontendIptvSettingsFecType;
using ::aidl::android::hardware::tv::tuner::FrontendModulation;
using ::aidl::android::hardware::tv::tuner::FrontendModulationStatus;
using ::aidl::android::hardware::tv::tuner::FrontendRollOff;
@@ -3430,6 +3440,106 @@
return frontendSettings;
}
+static DemuxIpAddress getDemuxIpAddress(JNIEnv *env, const jobject& config, const char* className) {
+ jclass clazz = env->FindClass(className);
+
+ jbyteArray jsrcIpAddress = static_cast<jbyteArray>(
+ env->GetObjectField(config, env->GetFieldID(clazz, "mSrcIpAddress", "[B")));
+ jsize srcSize = env->GetArrayLength(jsrcIpAddress);
+ jbyteArray jdstIpAddress = static_cast<jbyteArray>(
+ env->GetObjectField(config, env->GetFieldID(clazz, "mDstIpAddress", "[B")));
+ jsize dstSize = env->GetArrayLength(jdstIpAddress);
+
+ DemuxIpAddress res;
+
+ if (srcSize != dstSize) {
+ // should never happen. Validated on Java size.
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "IP address lengths don't match. srcLength=%d, dstLength=%d", srcSize, dstSize);
+ return res;
+ }
+
+ if (srcSize == IP_V4_LENGTH) {
+ vector<uint8_t> srcAddr;
+ vector<uint8_t> dstAddr;
+ srcAddr.resize(IP_V4_LENGTH);
+ dstAddr.resize(IP_V4_LENGTH);
+ env->GetByteArrayRegion(jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte *>(&srcAddr[0]));
+ env->GetByteArrayRegion(jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte *>(&dstAddr[0]));
+ res.srcIpAddress.set<DemuxIpAddressIpAddress::Tag::v4>(srcAddr);
+ res.dstIpAddress.set<DemuxIpAddressIpAddress::Tag::v4>(dstAddr);
+ } else if (srcSize == IP_V6_LENGTH) {
+ vector<uint8_t> srcAddr;
+ vector<uint8_t> dstAddr;
+ srcAddr.resize(IP_V6_LENGTH);
+ dstAddr.resize(IP_V6_LENGTH);
+ env->GetByteArrayRegion(jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte *>(&srcAddr[0]));
+ env->GetByteArrayRegion(jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte *>(&dstAddr[0]));
+ res.srcIpAddress.set<DemuxIpAddressIpAddress::Tag::v6>(srcAddr);
+ res.dstIpAddress.set<DemuxIpAddressIpAddress::Tag::v6>(dstAddr);
+ } else {
+ // should never happen. Validated on Java size.
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "Invalid IP address length %d", srcSize);
+ return res;
+ }
+
+ res.srcPort = env->GetIntField(config, env->GetFieldID(clazz, "mSrcPort", "I"));
+ res.dstPort = env->GetIntField(config, env->GetFieldID(clazz, "mDstPort", "I"));
+
+ return res;
+}
+
+static FrontendIptvSettingsFec getIptvFrontendSettingsFec(JNIEnv *env, const jobject &settings) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IptvFrontendSettings");
+ jobject fec = env->GetObjectField(settings, env->GetFieldID(clazz, "mFec",
+ "[Landroid/media/tv/tuner/frontend/IptvFrontendSettingsFec;"));
+ jclass fecClazz = env->FindClass("android/media/tv/tuner/frontend/IptvFrontendSettingsFec");
+ FrontendIptvSettingsFecType fecType =
+ static_cast<FrontendIptvSettingsFecType>(
+ env->GetIntField(fec, env->GetFieldID(fecClazz, "mFec", "I")));
+ int32_t fecColNum = env->GetIntField(fec, env->GetFieldID(fecClazz, "mFecColNum", "I"));
+ int32_t fecRowNum = env->GetIntField(fec, env->GetFieldID(fecClazz, "mFecRowNum", "I"));
+
+ FrontendIptvSettingsFec frontendIptvSettingsFec {
+ .type = fecType,
+ .fecColNum = fecColNum,
+ .fecRowNum = fecRowNum,
+ };
+
+ return frontendIptvSettingsFec;
+}
+
+static FrontendSettings getIptvFrontendSettings(JNIEnv *env, const jobject &settings) {
+ FrontendSettings frontendSettings;
+ const char *clazzName = "android/media/tv/tuner/frontend/IptvFrontendSettings";
+ jclass clazz = env->FindClass(clazzName);
+ FrontendIptvSettingsProtocol protocol =
+ static_cast<FrontendIptvSettingsProtocol>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mProtocol", "I")));
+ FrontendIptvSettingsIgmp igmp =
+ static_cast<FrontendIptvSettingsIgmp>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mIgmp", "I")));
+ FrontendIptvSettingsFec fec = getIptvFrontendSettingsFec(env, settings);
+ int64_t bitrate = env->GetIntField(settings, env->GetFieldID(clazz, "mBitrate", "J"));
+ jstring contentUrlJString = (jstring) env->GetObjectField(settings, env->GetFieldID(
+ clazz, "mContentUrl",
+ "[Landroid/media/tv/tuner/frontend/IptvFrontendSettings;"));
+ const char *contentUrl = env->GetStringUTFChars(contentUrlJString, 0);
+ DemuxIpAddress ipAddr = getDemuxIpAddress(env, settings, clazzName);
+
+ FrontendIptvSettings frontendIptvSettings{
+ .protocol = protocol,
+ .fec = fec,
+ .igmp = igmp,
+ .bitrate = bitrate,
+ .ipAddr = ipAddr,
+ .contentUrl = contentUrl,
+ };
+ frontendSettings.set<FrontendSettings::Tag::iptv>(frontendIptvSettings);
+ return frontendSettings;
+}
+
static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject settings) {
ALOGV("getFrontendSettings %d", type);
FrontendType feType = static_cast<FrontendType>(type);
@@ -3454,6 +3564,8 @@
return getIsdbtFrontendSettings(env, settings);
case FrontendType::DTMB:
return getDtmbFrontendSettings(env, settings);
+ case FrontendType::IPTV:
+ return getIptvFrontendSettings(env, settings);
default:
// should never happen because a type is associated with a subclass of
// FrontendSettings and not set by users
@@ -3943,56 +4055,6 @@
return filterDownloadSettings;
}
-static DemuxIpAddress getDemuxIpAddress(JNIEnv *env, const jobject& config) {
- jclass clazz = env->FindClass("android/media/tv/tuner/filter/IpFilterConfiguration");
-
- jbyteArray jsrcIpAddress = static_cast<jbyteArray>(
- env->GetObjectField(config, env->GetFieldID(clazz, "mSrcIpAddress", "[B")));
- jsize srcSize = env->GetArrayLength(jsrcIpAddress);
- jbyteArray jdstIpAddress = static_cast<jbyteArray>(
- env->GetObjectField(config, env->GetFieldID(clazz, "mDstIpAddress", "[B")));
- jsize dstSize = env->GetArrayLength(jdstIpAddress);
-
- DemuxIpAddress res;
-
- if (srcSize != dstSize) {
- // should never happen. Validated on Java size.
- jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
- "IP address lengths don't match. srcLength=%d, dstLength=%d", srcSize, dstSize);
- return res;
- }
-
- if (srcSize == IP_V4_LENGTH) {
- vector<uint8_t> srcAddr;
- vector<uint8_t> dstAddr;
- srcAddr.resize(IP_V4_LENGTH);
- dstAddr.resize(IP_V4_LENGTH);
- env->GetByteArrayRegion(jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte *>(&srcAddr[0]));
- env->GetByteArrayRegion(jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte *>(&dstAddr[0]));
- res.srcIpAddress.set<DemuxIpAddressIpAddress::Tag::v4>(srcAddr);
- res.dstIpAddress.set<DemuxIpAddressIpAddress::Tag::v4>(dstAddr);
- } else if (srcSize == IP_V6_LENGTH) {
- vector<uint8_t> srcAddr;
- vector<uint8_t> dstAddr;
- srcAddr.resize(IP_V6_LENGTH);
- dstAddr.resize(IP_V6_LENGTH);
- env->GetByteArrayRegion(jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte *>(&srcAddr[0]));
- env->GetByteArrayRegion(jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte *>(&dstAddr[0]));
- res.srcIpAddress.set<DemuxIpAddressIpAddress::Tag::v6>(srcAddr);
- res.dstIpAddress.set<DemuxIpAddressIpAddress::Tag::v6>(dstAddr);
- } else {
- // should never happen. Validated on Java size.
- jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
- "Invalid IP address length %d", srcSize);
- return res;
- }
-
- res.srcPort = env->GetIntField(config, env->GetFieldID(clazz, "mSrcPort", "I"));
- res.dstPort = env->GetIntField(config, env->GetFieldID(clazz, "mDstPort", "I"));
-
- return res;
-}
-
static DemuxFilterSettings getFilterConfiguration(
JNIEnv *env, int type, int subtype, jobject filterConfigObj) {
DemuxFilterSettings filterSettings;
@@ -4088,7 +4150,8 @@
break;
}
case DemuxFilterMainType::IP: {
- DemuxIpAddress ipAddr = getDemuxIpAddress(env, filterConfigObj);
+ DemuxIpAddress ipAddr = getDemuxIpAddress(env, filterConfigObj,
+ "android/media/tv/tuner/filter/IpFilterConfiguration");
DemuxIpFilterSettings ipFilterSettings {
.ipAddr = ipAddr,
};
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
index aad708a..0538e7d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -42,6 +42,7 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.view.View;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull;
@@ -66,6 +67,9 @@
private static final float DEFAULT_MENU_POSITION_X_PERCENT = 1.0f;
@FloatRange(from = 0.0, to = 1.0)
+ private static final float DEFAULT_MENU_POSITION_X_PERCENT_RTL = 0.0f;
+
+ @FloatRange(from = 0.0, to = 1.0)
private static final float DEFAULT_MENU_POSITION_Y_PERCENT = 0.77f;
private static final boolean DEFAULT_MOVE_TO_TUCKED_VALUE = false;
private static final boolean DEFAULT_HAS_SEEN_DOCK_TOOLTIP_VALUE = false;
@@ -226,8 +230,12 @@
final String absolutePositionString = Prefs.getString(mContext,
Prefs.Key.ACCESSIBILITY_FLOATING_MENU_POSITION, /* defaultValue= */ null);
+ final float defaultPositionXPercent =
+ mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+ ? DEFAULT_MENU_POSITION_X_PERCENT_RTL
+ : DEFAULT_MENU_POSITION_X_PERCENT;
return TextUtils.isEmpty(absolutePositionString)
- ? new Position(DEFAULT_MENU_POSITION_X_PERCENT, DEFAULT_MENU_POSITION_Y_PERCENT)
+ ? new Position(defaultPositionXPercent, DEFAULT_MENU_POSITION_Y_PERCENT)
: Position.fromString(absolutePositionString);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index e42f051..3e5d16a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -878,7 +878,7 @@
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
windowFlags,
PixelFormat.TRANSLUCENT);
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index e9d7a5b..3319f9d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -49,6 +49,7 @@
import kotlin.math.sqrt
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
@@ -163,12 +164,26 @@
ambientIndicationArea?.alpha = alpha
indicationArea.alpha = alpha
- startButton.alpha = alpha
- endButton.alpha = alpha
}
}
launch {
+ updateButtonAlpha(
+ view = startButton,
+ viewModel = viewModel.startButton,
+ alphaFlow = viewModel.alpha,
+ )
+ }
+
+ launch {
+ updateButtonAlpha(
+ view = endButton,
+ viewModel = viewModel.endButton,
+ alphaFlow = viewModel.alpha,
+ )
+ }
+
+ launch {
viewModel.indicationAreaTranslationX.collect { translationX ->
indicationArea.translationX = translationX
ambientIndicationArea?.translationX = translationX
@@ -321,7 +336,6 @@
.animate()
.scaleX(if (viewModel.isSelected) SCALE_SELECTED_BUTTON else 1f)
.scaleY(if (viewModel.isSelected) SCALE_SELECTED_BUTTON else 1f)
- .alpha(if (viewModel.isDimmed) DIM_ALPHA else 1f)
.start()
view.isClickable = viewModel.isClickable
@@ -341,6 +355,17 @@
view.isSelected = viewModel.isSelected
}
+ private suspend fun updateButtonAlpha(
+ view: View,
+ viewModel: Flow<KeyguardQuickAffordanceViewModel>,
+ alphaFlow: Flow<Float>,
+ ) {
+ combine(viewModel.map { it.isDimmed }, alphaFlow) { isDimmed, alpha ->
+ if (isDimmed) DIM_ALPHA else alpha
+ }
+ .collect { view.alpha = it }
+ }
+
private class OnTouchListener(
private val view: View,
private val viewModel: KeyguardQuickAffordanceViewModel,
diff --git a/rs/jni/Android.bp b/rs/jni/Android.bp
index 9a6fa8e..8a6897c 100644
--- a/rs/jni/Android.bp
+++ b/rs/jni/Android.bp
@@ -51,4 +51,10 @@
"-Wunreachable-code",
"-Wno-deprecated-declarations",
],
+
+ target: {
+ android_riscv64: {
+ enabled: false,
+ },
+ },
}
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index e06ce2c9..4ad43fa 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1831,7 +1831,7 @@
final BatteryStatsImpl bstats = mService.mBatteryStatsService.getActiveStatistics();
synchronized (bstats) {
if (haveNewCpuStats) {
- if (bstats.startAddingCpuLocked()) {
+ if (bstats.startAddingCpuStatsLocked()) {
int totalUTime = 0;
int totalSTime = 0;
final int statsCount = mProcessCpuTracker.countStats();
@@ -1877,9 +1877,10 @@
final int irqTime = mProcessCpuTracker.getLastIrqTime();
final int softIrqTime = mProcessCpuTracker.getLastSoftIrqTime();
final int idleTime = mProcessCpuTracker.getLastIdleTime();
- bstats.finishAddingCpuLocked(totalUTime, totalSTime, userTime,
+ bstats.addCpuStatsLocked(totalUTime, totalSTime, userTime,
systemTime, iowaitTime, irqTime, softIrqTime, idleTime);
}
+ bstats.finishAddingCpuStatsLocked();
}
if (mLastWriteTime < (now - BATTERY_STATS_TIME)) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 42e0978..3759a8b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2396,7 +2396,9 @@
}
// fallthrough
default:
- Slog.w(TAG, "Display " + info + " does not support input device matching.");
+ if (DEBUG) {
+ Slog.w(TAG, "Display " + info + " does not support input device matching.");
+ }
}
return Optional.empty();
}
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index f8d6c5f..52e20d6 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -635,7 +635,9 @@
// which is within the render rate range
// - 90hz is not in range as none of the even divisors (i.e. 90, 45, 30)
// fall within the acceptable render range.
- final int divisor = (int) Math.ceil(physicalRefreshRate / summary.maxRenderFrameRate);
+ final int divisor =
+ (int) Math.ceil((physicalRefreshRate / summary.maxRenderFrameRate)
+ - FLOAT_TOLERANCE);
float adjustedPhysicalRefreshRate = physicalRefreshRate / divisor;
if (adjustedPhysicalRefreshRate < (summary.minRenderFrameRate - FLOAT_TOLERANCE)) {
if (mLoggingEnabled) {
@@ -3081,10 +3083,10 @@
@Override
public boolean supportsFrameRateOverride() {
- return SurfaceFlingerProperties.enable_frame_rate_override().orElse(false)
+ return SurfaceFlingerProperties.enable_frame_rate_override().orElse(true)
&& !SurfaceFlingerProperties.frame_rate_override_for_native_rates()
- .orElse(true)
- && SurfaceFlingerProperties.frame_rate_override_global().orElse(false);
+ .orElse(false)
+ && SurfaceFlingerProperties.frame_rate_override_global().orElse(true);
}
private DisplayManager getDisplayManager() {
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index f870c0a..d7306b7 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -119,6 +119,8 @@
// are yet to be delivered.
options.setDeliveryGroupMatchingKey(
DREAMING_DELIVERY_GROUP_NAMESPACE, DREAMING_DELIVERY_GROUP_KEY);
+ // This allows the broadcast delivery to be delayed to apps in the Cached state.
+ options.setDeferUntilActive(true);
return options.toBundle();
}
diff --git a/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java b/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
index 6e86123..5bdcbb9 100644
--- a/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
+++ b/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
@@ -36,6 +36,7 @@
import android.util.IndentingPrintWriter;
import android.util.Slog;
+import com.android.internal.os.RoSystemProperties;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import com.android.server.UiThread;
@@ -100,6 +101,11 @@
pw.println(" --reboot (which does a full reboot) or");
pw.println(" --no-restart (which requires a manual restart)");
pw.println();
+ pw.println(" is-headless-system-user-mode [-v | --verbose]");
+ pw.println(" Checks whether the device uses headless system user mode.");
+ pw.println(" It returns the effective mode, even when using emulation");
+ pw.println(" (to get the real mode as well, use -v or --verbose)");
+ pw.println();
pw.println(" is-user-visible [--display DISPLAY_ID] <USER_ID>");
pw.println(" Checks if the given user is visible in the given display.");
pw.println(" If the display option is not set, it uses the user's context to check");
@@ -121,6 +127,8 @@
return runReportPackageAllowlistProblems();
case "set-system-user-mode-emulation":
return runSetSystemUserModeEmulation();
+ case "is-headless-system-user-mode":
+ return runIsHeadlessSystemUserMode();
case "is-user-visible":
return runIsUserVisible();
default:
@@ -407,6 +415,35 @@
return 0;
}
+ private int runIsHeadlessSystemUserMode() {
+ PrintWriter pw = getOutPrintWriter();
+
+ boolean verbose = false;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-v":
+ case "--verbose":
+ verbose = true;
+ break;
+ default:
+ pw.println("Invalid option: " + opt);
+ return -1;
+ }
+ }
+
+ boolean isHsum = mService.isHeadlessSystemUserMode();
+ if (!verbose) {
+ // NOTE: do not change output below, as it's used by ITestDevice
+ // (it's ok to change the verbose option though)
+ pw.println(isHsum);
+ } else {
+ pw.printf("effective=%b real=%b\n", isHsum,
+ RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER);
+ }
+ return 0;
+ }
+
/**
* Gets the {@link UserManager} associated with the context of the given user.
*/
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 89c5c9e..bc90c89 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -3925,6 +3925,7 @@
private final HistoryStepDetails mDetails = new HistoryStepDetails();
private boolean mHasHistoryStepDetails;
+ private boolean mUpdateRequested;
/**
* Total time (in milliseconds) spent executing in user code.
@@ -3954,15 +3955,20 @@
@Override
public HistoryStepDetails getHistoryStepDetails() {
- // Perform a CPU update right after we do this collection, so we have started
- // collecting good data for the next step.
- requestImmediateCpuUpdate();
+ if (!mUpdateRequested) {
+ mUpdateRequested = true;
+ // Perform a CPU update right after we do this collection, so we have started
+ // collecting good data for the next step.
+ requestImmediateCpuUpdate();
- if (mPlatformIdleStateCallback != null) {
- mDetails.statSubsystemPowerState =
- mPlatformIdleStateCallback.getSubsystemLowPowerStats();
- if (DEBUG) Slog.i(TAG, "WRITE SubsystemPowerState:" +
- mDetails.statSubsystemPowerState);
+ if (mPlatformIdleStateCallback != null) {
+ mDetails.statSubsystemPowerState =
+ mPlatformIdleStateCallback.getSubsystemLowPowerStats();
+ if (DEBUG) {
+ Slog.i(TAG,
+ "WRITE SubsystemPowerState:" + mDetails.statSubsystemPowerState);
+ }
+ }
}
if (!mHasHistoryStepDetails) {
@@ -4072,7 +4078,11 @@
mCurStepStatIrqTimeMs += statIrqTimeMs;
mCurStepStatSoftIrqTimeMs += statSoftIrqTimeMs;
mCurStepStatIdleTimeMs += statIdleTimeMs;
+ }
+
+ public void finishAddingCpuLocked() {
mHasHistoryStepDetails = true;
+ mUpdateRequested = false;
}
@Override
@@ -4953,13 +4963,13 @@
}
@GuardedBy("this")
- public boolean startAddingCpuLocked() {
+ public boolean startAddingCpuStatsLocked() {
mExternalSync.cancelCpuSyncDueToWakelockChange();
return mOnBatteryInternal;
}
@GuardedBy("this")
- public void finishAddingCpuLocked(int totalUTimeMs, int totalSTimeMs, int statUserTimeMs,
+ public void addCpuStatsLocked(int totalUTimeMs, int totalSTimeMs, int statUserTimeMs,
int statSystemTimeMs, int statIOWaitTimeMs, int statIrqTimeMs,
int statSoftIrqTimeMs, int statIdleTimeMs) {
mStepDetailsCalculator.addCpuStats(totalUTimeMs, totalSTimeMs, statUserTimeMs,
@@ -4967,6 +4977,14 @@
statSoftIrqTimeMs, statIdleTimeMs);
}
+ /**
+ * Called after {@link #addCpuStatsLocked} has been invoked for all active apps.
+ */
+ @GuardedBy("this")
+ public void finishAddingCpuStatsLocked() {
+ mStepDetailsCalculator.finishAddingCpuLocked();
+ }
+
public void noteProcessDiedLocked(int uid, int pid) {
uid = mapUid(uid);
Uid u = mUidStats.get(uid);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
old mode 100755
new mode 100644
index 29b37ce..0928bef
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -24,6 +24,7 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -43,6 +44,7 @@
import android.graphics.Rect;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
+import android.media.AudioPresentation;
import android.media.PlaybackParams;
import android.media.tv.AdBuffer;
import android.media.tv.AdRequest;
@@ -91,7 +93,6 @@
import android.util.SparseArray;
import android.view.InputChannel;
import android.view.Surface;
-
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
@@ -102,9 +103,7 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.IoThread;
import com.android.server.SystemService;
-
import dalvik.annotation.optimization.NeverCompile;
-
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
@@ -814,7 +813,7 @@
@GuardedBy("mLock")
private boolean createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
- int userId) {
+ int userId, AttributionSource tvAppAttributionSource) {
UserState userState = getOrCreateUserStateLocked(userId);
SessionState sessionState = userState.sessionStateMap.get(sessionToken);
if (DEBUG) {
@@ -833,8 +832,8 @@
service.createRecordingSession(
callback, sessionState.inputId, sessionState.sessionId);
} else {
- service.createSession(
- channels[1], callback, sessionState.inputId, sessionState.sessionId);
+ service.createSession(channels[1], callback, sessionState.inputId,
+ sessionState.sessionId, tvAppAttributionSource);
}
} catch (RemoteException e) {
Slog.e(TAG, "error in createSession", e);
@@ -1486,7 +1485,8 @@
@Override
public void createSession(final ITvInputClient client, final String inputId,
- boolean isRecordingSession, int seq, int userId) {
+ AttributionSource tvAppAttributionSource, boolean isRecordingSession, int seq,
+ int userId) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int resolvedUserId = resolveCallingUserId(callingPid, callingUid,
@@ -1557,7 +1557,7 @@
if (serviceState.service != null) {
if (!createSessionInternalLocked(serviceState.service, sessionToken,
- resolvedUserId)) {
+ resolvedUserId, tvAppAttributionSource)) {
removeSessionStateLocked(sessionToken, resolvedUserId);
}
} else {
@@ -1836,6 +1836,28 @@
}
@Override
+ public void selectAudioPresentation(IBinder sessionToken, int presentationId,
+ int programId, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "selectAudioPresentation");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid,
+ resolvedUserId).selectAudioPresentation(
+ presentationId, programId);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slog.e(TAG, "error in selectAudioPresentation", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void selectTrack(IBinder sessionToken, int type, String trackId, int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
@@ -3137,7 +3159,8 @@
// And create sessions, if any.
for (IBinder sessionToken : serviceState.sessionTokens) {
- if (!createSessionInternalLocked(serviceState.service, sessionToken, mUserId)) {
+ if (!createSessionInternalLocked(
+ serviceState.service, sessionToken, mUserId, null)) {
tokensToBeRemoved.add(sessionToken);
}
}
@@ -3360,6 +3383,43 @@
}
@Override
+ public void onAudioPresentationsChanged(List<AudioPresentation> audioPresentations) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onAudioPresentationChanged(" + audioPresentations + ")");
+ }
+ if (mSessionState.session == null || mSessionState.client == null) {
+ return;
+ }
+ try {
+ mSessionState.client.onAudioPresentationsChanged(audioPresentations,
+ mSessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onAudioPresentationsChanged", e);
+ }
+ }
+ }
+
+ @Override
+ public void onAudioPresentationSelected(int presentationId, int programId) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onAudioPresentationSelected(presentationId=" + presentationId
+ + ", programId=" + programId + ")");
+ }
+ if (mSessionState.session == null || mSessionState.client == null) {
+ return;
+ }
+ try {
+ mSessionState.client.onAudioPresentationSelected(presentationId, programId,
+ mSessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onAudioPresentationSelected", e);
+ }
+ }
+ }
+
+ @Override
public void onTracksChanged(List<TvTrackInfo> tracks) {
synchronized (mLock) {
if (DEBUG) {
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.cpp b/services/core/jni/gnss/GnssMeasurementCallback.cpp
index a1c5708..2982546 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.cpp
+++ b/services/core/jni/gnss/GnssMeasurementCallback.cpp
@@ -58,7 +58,7 @@
jmethodID method_gnssAgcBuilderBuild;
jmethodID method_gnssMeasurementsEventBuilderCtor;
jmethodID method_gnssMeasurementsEventBuilderSetClock;
-jmethodID method_gnssMeasurementsEventBuilderSetFullTracking;
+jmethodID method_gnssMeasurementsEventBuilderSetIsFullTracking;
jmethodID method_gnssMeasurementsEventBuilderSetMeasurements;
jmethodID method_gnssMeasurementsEventBuilderSetGnssAutomaticGainControls;
jmethodID method_gnssMeasurementsEventBuilderBuild;
@@ -110,8 +110,8 @@
env->GetMethodID(class_gnssMeasurementsEventBuilder, "setGnssAutomaticGainControls",
"([Landroid/location/GnssAutomaticGainControl;)"
"Landroid/location/GnssMeasurementsEvent$Builder;");
- method_gnssMeasurementsEventBuilderSetFullTracking =
- env->GetMethodID(class_gnssMeasurementsEventBuilder, "setFullTracking",
+ method_gnssMeasurementsEventBuilderSetIsFullTracking =
+ env->GetMethodID(class_gnssMeasurementsEventBuilder, "setIsFullTracking",
"(Z)"
"Landroid/location/GnssMeasurementsEvent$Builder;");
method_gnssMeasurementsEventBuilderBuild =
@@ -234,7 +234,7 @@
void setMeasurementData(JNIEnv* env, jobject& callbacksObj, jobject clock,
jobjectArray measurementArray, jobjectArray gnssAgcArray,
- bool hasFullTracking, jboolean isFullTracking) {
+ bool hasIsFullTracking, jboolean isFullTracking) {
jobject gnssMeasurementsEventBuilderObject =
env->NewObject(class_gnssMeasurementsEventBuilder,
method_gnssMeasurementsEventBuilderCtor);
@@ -246,9 +246,9 @@
callObjectMethodIgnoringResult(env, gnssMeasurementsEventBuilderObject,
method_gnssMeasurementsEventBuilderSetGnssAutomaticGainControls,
gnssAgcArray);
- if (hasFullTracking) {
+ if (hasIsFullTracking) {
callObjectMethodIgnoringResult(env, gnssMeasurementsEventBuilderObject,
- method_gnssMeasurementsEventBuilderSetFullTracking,
+ method_gnssMeasurementsEventBuilderSetIsFullTracking,
isFullTracking);
}
jobject gnssMeasurementsEventObject =
@@ -394,10 +394,10 @@
gnssAgcArray = translateAllGnssAgcs(env, data.gnssAgcs);
if (this->getInterfaceVersion() >= 3) {
setMeasurementData(env, mCallbacksObj, clock, measurementArray, gnssAgcArray,
- /*hasFullTracking=*/true, data.isFullTracking);
+ /*hasIsFullTracking=*/true, data.isFullTracking);
} else {
setMeasurementData(env, mCallbacksObj, clock, measurementArray, gnssAgcArray,
- /*hasFullTracking=*/false,
+ /*hasIsFullTracking=*/false,
/*isFullTracking=*/JNI_FALSE);
}
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.h b/services/core/jni/gnss/GnssMeasurementCallback.h
index fde56881..b3de486 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.h
+++ b/services/core/jni/gnss/GnssMeasurementCallback.h
@@ -49,7 +49,7 @@
void setMeasurementData(JNIEnv* env, jobject& callbacksObj, jobject clock,
jobjectArray measurementArray, jobjectArray gnssAgcArray,
- bool hasFullTracking, jboolean isFullTracking);
+ bool hasIsFullTracking, jboolean isFullTracking);
class GnssMeasurementCallbackAidl : public hardware::gnss::BnGnssMeasurementCallback {
public:
@@ -142,7 +142,7 @@
jobjectArray measurementArray =
translateAllGnssMeasurements(env, data.measurements.data(), count);
setMeasurementData(env, mCallbacksObj, clock, measurementArray, /*gnssAgcArray=*/nullptr,
- /*hasFullTracking=*/false,
+ /*hasIsFullTracking=*/false,
/*isFullTracking=*/JNI_FALSE);
env->DeleteLocalRef(clock);
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index f915298..101498a 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -92,7 +92,10 @@
certificate: "platform",
platform_apis: true,
- test_suites: ["device-tests"],
+ test_suites: [
+ "device-tests",
+ "automotive-tests",
+ ],
optimize: {
enabled: false,
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 57c5a6e..ef470fe 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -208,6 +208,7 @@
private static final String TAG = AlarmManagerServiceTest.class.getSimpleName();
private static final int SYSTEM_UI_UID = 12345;
private static final int TEST_CALLING_USER = UserHandle.getUserId(TEST_CALLING_UID);
+ private static final int TEST_CALLING_UID_2 = TEST_CALLING_UID + 1;
private long mAppStandbyWindow;
private long mAllowWhileIdleWindow;
@@ -3412,10 +3413,40 @@
final int type = ((i & 1) == 0) ? ELAPSED_REALTIME : ELAPSED_REALTIME_WAKEUP;
setTestAlarm(type, mNowElapsedTest + i, getNewMockPendingIntent());
}
+ for (int i = 0; i < 4; i++) {
+ final int type = ((i & 1) == 0) ? ELAPSED_REALTIME : ELAPSED_REALTIME_WAKEUP;
+ setTestAlarm(
+ type,
+ mNowElapsedTest + i,
+ getNewMockPendingIntent(),
+ 0,
+ FLAG_STANDALONE,
+ TEST_CALLING_UID_2);
+ }
mNowElapsedTest += 100;
mTestTimer.expire();
- verify(() -> MetricsHelper.pushAlarmBatchDelivered(10, 5));
+ final ArgumentCaptor<int[]> uidsCaptor = ArgumentCaptor.forClass(int[].class);
+ final ArgumentCaptor<int[]> alarmsPerUidCaptor = ArgumentCaptor.forClass(int[].class);
+ final ArgumentCaptor<int[]> wakeupAlarmsPerUidCaptor = ArgumentCaptor.forClass(int[].class);
+
+ verify(() -> MetricsHelper.pushAlarmBatchDelivered(
+ eq(14),
+ eq(7),
+ uidsCaptor.capture(),
+ alarmsPerUidCaptor.capture(),
+ wakeupAlarmsPerUidCaptor.capture()));
+ assertEquals(2, uidsCaptor.getValue().length);
+ assertEquals(2, alarmsPerUidCaptor.getValue().length);
+ assertEquals(2, wakeupAlarmsPerUidCaptor.getValue().length);
+ final int uid1Idx = uidsCaptor.getValue()[0] == TEST_CALLING_UID ? 0 : 1;
+ final int uid2Idx = 1 - uid1Idx;
+ assertEquals(TEST_CALLING_UID, uidsCaptor.getValue()[uid1Idx]);
+ assertEquals(TEST_CALLING_UID_2, uidsCaptor.getValue()[uid2Idx]);
+ assertEquals(10, alarmsPerUidCaptor.getValue()[uid1Idx]);
+ assertEquals(5, wakeupAlarmsPerUidCaptor.getValue()[uid1Idx]);
+ assertEquals(4, alarmsPerUidCaptor.getValue()[uid2Idx]);
+ assertEquals(2, wakeupAlarmsPerUidCaptor.getValue()[uid2Idx]);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 69a0b87..3a3a507 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -298,6 +298,18 @@
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.baseModeId).isEqualTo(60);
+
+ votes.clear();
+ votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
+ Vote.forRenderFrameRates(0, 60 - error));
+ votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
+ Vote.forRenderFrameRates(60 + error, Float.POSITIVE_INFINITY));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+
+ assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(60);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
index 74189fd..4fde73b 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
@@ -41,10 +41,10 @@
public class BatteryStatsHistoryIteratorTest {
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
- private MockClock mMockClock = new MockClock();
+ private final MockClock mMockClock = new MockClock();
private MockBatteryStatsImpl mBatteryStats;
- private Random mRandom = new Random();
- private MockExternalStatsSync mExternalStatsSync = new MockExternalStatsSync();
+ private final Random mRandom = new Random();
+ private final MockExternalStatsSync mExternalStatsSync = new MockExternalStatsSync();
@Before
public void setup() {
@@ -184,29 +184,27 @@
100, /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0,
1_000_000, 1_000_000, 1_000_000);
- assertThat(mExternalStatsSync.mSyncScheduled).isTrue();
- mBatteryStats.finishAddingCpuLocked(100, 0, 0, 0, 0, 0, 0, 0);
- mExternalStatsSync.mSyncScheduled = false;
+ mExternalStatsSync.updateCpuStats(100, 1_100_000, 1_100_000);
// Device was suspended for 3_000 seconds, note the difference in elapsed time and uptime
mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
100, /* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0,
5_000_000, 2_000_000, 5_000_000);
- assertThat(mExternalStatsSync.mSyncScheduled).isTrue();
- mBatteryStats.finishAddingCpuLocked(200, 0, 0, 0, 0, 0, 0, 0);
- mExternalStatsSync.mSyncScheduled = false;
+ mExternalStatsSync.updateCpuStats(200, 5_100_000, 2_100_000);
// Battery level is unchanged, so we don't write battery level details in history
mBatteryStats.noteAlarmStartLocked("wakeup", null, APP_UID, 6_000_000, 3_000_000);
- assertThat(mExternalStatsSync.mSyncScheduled).isFalse();
+ assertThat(mExternalStatsSync.isSyncScheduled()).isFalse();
// Battery level drops, so we write the accumulated battery level details
mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
100, /* plugType */ 0, 79, 72, 3700, 2_000_000, 4_000_000, 0,
7_000_000, 4_000_000, 6_000_000);
+ mExternalStatsSync.updateCpuStats(300, 7_100_000, 4_100_000);
+
final BatteryStatsHistoryIterator iterator = mBatteryStats.iterateBatteryStatsHistory();
BatteryStats.HistoryItem item;
@@ -220,6 +218,10 @@
assertThat(item = iterator.next()).isNotNull();
assertThat(item.batteryLevel).isEqualTo(90);
+ assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS);
+
+ assertThat(item = iterator.next()).isNotNull();
+ assertThat(item.batteryLevel).isEqualTo(90);
assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isEqualTo(0);
assertThat(item.stepDetails.userTime).isEqualTo(100);
@@ -230,6 +232,10 @@
assertThat(item = iterator.next()).isNotNull();
assertThat(item.batteryLevel).isEqualTo(80);
+ assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS);
+
+ assertThat(item = iterator.next()).isNotNull();
+ assertThat(item.batteryLevel).isEqualTo(80);
assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_ALARM_START);
assertThat(item.stepDetails).isNull();
@@ -238,6 +244,10 @@
assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isNotEqualTo(0);
assertThat(item.stepDetails.userTime).isEqualTo(200);
+ assertThat(item = iterator.next()).isNotNull();
+ assertThat(item.batteryLevel).isEqualTo(79);
+ assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS);
+
assertThat(item = iterator.next()).isNull();
}
@@ -258,7 +268,7 @@
assertThat(item.time).isEqualTo(elapsedTimeMs);
}
- private static class MockExternalStatsSync extends MockBatteryStatsImpl.DummyExternalStatsSync {
+ private class MockExternalStatsSync extends MockBatteryStatsImpl.DummyExternalStatsSync {
private boolean mSyncScheduled;
@Override
@@ -266,5 +276,18 @@
mSyncScheduled = true;
return null;
}
+
+ public boolean isSyncScheduled() {
+ return mSyncScheduled;
+ }
+
+ public void updateCpuStats(int totalUTimeMs, long elapsedRealtime, long uptime) {
+ assertThat(mExternalStatsSync.mSyncScheduled).isTrue();
+ mBatteryStats.recordHistoryEventLocked(elapsedRealtime, uptime,
+ BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS, "wakelock-update", 0);
+ mBatteryStats.addCpuStatsLocked(totalUTimeMs, 0, 0, 0, 0, 0, 0, 0);
+ mBatteryStats.finishAddingCpuStatsLocked();
+ mExternalStatsSync.mSyncScheduled = false;
+ }
}
}
diff --git a/telecomm/java/android/telecom/CallEndpointException.java b/telecomm/java/android/telecom/CallEndpointException.java
index e2238928..994e1c9 100644
--- a/telecomm/java/android/telecom/CallEndpointException.java
+++ b/telecomm/java/android/telecom/CallEndpointException.java
@@ -92,25 +92,12 @@
public @interface CallEndpointErrorCode {
}
- public CallEndpointException(@Nullable String message) {
- super(getMessage(message, ERROR_UNSPECIFIED));
- mMessage = message;
- }
-
public CallEndpointException(@Nullable String message, @CallEndpointErrorCode int code) {
super(getMessage(message, code));
mCode = code;
mMessage = message;
}
- public CallEndpointException(@Nullable String message, @CallEndpointErrorCode int code,
- @Nullable Throwable cause) {
- super(getMessage(message, code), cause);
- mCode = code;
- mMessage = message;
- }
-
-
public @CallEndpointErrorCode int getCode() {
return mCode;
}